面試專題我放在git上了,地址Github 歡迎fork然后一起更新
1. 內(nèi)存模型以及分區(qū)膜廊,需要詳細(xì)到每個區(qū)放什么乏沸。
Java程序運行的內(nèi)存分配策略有三種,分別是靜態(tài)分配爪瓜,棧式分配蹬跃,堆式分配。對應(yīng)的三種策略使用的內(nèi)存空間主要分別是堆區(qū)铆铆,棧區(qū)和方法區(qū)蝶缀;
方法區(qū):主要是存儲靜態(tài)數(shù)據(jù),類信息薄货,全局static數(shù)據(jù)和常量翁都,編譯后的代碼(字節(jié)碼)等數(shù)據(jù)
堆:又稱動態(tài)內(nèi)存分配,通常程序運行時直接new出來的對象谅猾,成員變量 (那種非 static 的變量)柄慰,所有的對象實例和數(shù)組都要在堆上分配
棧:棧的結(jié)構(gòu)是棧幀組成的,調(diào)用一個方法就壓入一幀税娜,幀上面存儲局部變量表坐搔,操作數(shù)棧,方法出口等信息敬矩,局部變量表存放的是 8 大基礎(chǔ)類型加上一個應(yīng)用類型,所以還是一個指向地址的指針弧岳;當(dāng)方法被執(zhí)行時凳忙,方法體內(nèi)的局部變量(其中包括基礎(chǔ)數(shù)據(jù)類型、對象的引用)都在棧上創(chuàng)建禽炬,并在方法執(zhí)行結(jié)束時這些局部變量所持有的內(nèi)存將會自動被釋放消略。
因為棧內(nèi)存分配運算內(nèi)置于處理器的指令集中,效率很高瞎抛,但是分配的內(nèi)存容量有限艺演。
- 本地方法棧:主要為 Native 方法服務(wù)
- 程序計數(shù)器:記錄當(dāng)前線程執(zhí)行的行號
2. 堆里面的分區(qū):Eden,survival (from+ to),老年代胎撤,各自的特點晓殊。
堆里面分為新生代和老生代(java8 取消了永久代,采用了 Metaspace)伤提,新生代包含 Eden+Survivor 區(qū)巫俺,survivor 區(qū)里面分為 from 和 to 區(qū),內(nèi)存回收時肿男,如果用的是復(fù)制算法介汹,從 from 復(fù)制到 to,當(dāng)經(jīng)過一次或者多次 GC 之后舶沛,存活下來的對象會被移動到老年區(qū)嘹承,當(dāng) JVM 內(nèi)存不夠用的時候,會觸發(fā) Full GC如庭,清理 JVM 老年區(qū)當(dāng)新生區(qū)滿了之后會觸發(fā) YGC,先把存活的對象放到其中一個 Survice區(qū)叹卷,然后進(jìn)行垃圾清理。因為如果僅僅清理需要刪除的對象坪它,這樣會導(dǎo)致內(nèi)存碎片骤竹,因此一般會把 Eden 進(jìn)行完全的清理,然后整理內(nèi)存往毡。那么下次 GC 的時候蒙揣,就會使用下一個 Survive,這樣循環(huán)使用开瞭。如果有特別大的對象鸣奔,新生代放不下,就會使用老年代的擔(dān)保惩阶,直接放到老年代里面挎狸。因為 JVM 認(rèn)為,一般大對象的存活時間一般比較久遠(yuǎn)断楷。
3. 對象創(chuàng)建方法锨匆,對象的內(nèi)存分配,對象的訪問定位冬筒。
new 一個對象
4. GC 的兩種判定方法
引用計數(shù)法:指的是如果某個地方引用了這個對象就+1恐锣,如果失效了就-1,當(dāng)為 0 就會回收但是 JVM 沒有用這種方式舞痰,因為無法判定相互循環(huán)引用(A 引用 B,B 引用 A)的情況
引用鏈法: 通過一種 GC ROOT 的對象(方法區(qū)中靜態(tài)變量引用的對象等-static 變量)來判斷土榴,如果有一條鏈能夠到達(dá) GC ROOT 就說明,不能到達(dá) GC ROOT 就說明可以回收
擴展:
哪些情況下的對象會被垃圾回收機制處理掉响牛?
利用可達(dá)性分析算法玷禽,虛擬機會將一些對象定義為GC Roots赫段,從GC Roots出發(fā)沿著引用鏈向下尋找,如果某個對象不能通過GC Roots尋找到矢赁,虛擬機就認(rèn)為該對象可以被回收掉糯笙。
哪些對象可以被看做是GC Roots呢?
1)虛擬機棧(棧幀中的本地變量表)中引用的對象撩银;
2)方法區(qū)中的類靜態(tài)屬性引用的對象给涕,常量引用的對象;
3)本地方法棧中JNI(Native方法)引用的對象额获;
對象不可達(dá)够庙,一定會被垃圾收集器回收么?
即使不可達(dá)抄邀,對象也不一定會被垃圾收集器回收耘眨,1)先判斷對象是否有必要執(zhí)行finalize()方法,對象必須重寫finalize()方法且沒有被運行過撤摸。2)若有必要執(zhí)行,會把對象放到一個隊列中褒纲,JVM會開一個線程去回收它們准夷,這是對象最后一次可以逃逸清理的機會。
5. SafePoint 是什么
比如 GC 的時候必須要等到 Java 線程都進(jìn)入到 safepoint 的時候 VMThread 才能開始執(zhí)行 GC莺掠,
1. 循環(huán)的末尾 (防止大循環(huán)的時候一直不進(jìn)入 safepoint衫嵌,而其他線程在等待它進(jìn)入safepoint)
2. 方法返回前
3. 調(diào)用方法的 call 之后
4. 拋出異常的位置
6. GC 的三種收集方法:標(biāo)記清除、標(biāo)記整理彻秆、復(fù)制算法的原理與特點楔绞,分別用在什么地方,如果讓你優(yōu)化收集方法唇兑,有什么思路酒朵?
先標(biāo)記,標(biāo)記完畢之后再清除扎附,效率不高蔫耽,會產(chǎn)生碎片
復(fù)制算法:分為 8:1 的 Eden 區(qū)和 survivor 區(qū),就是上面談到的 YGC
標(biāo)記整理:標(biāo)記完畢之后留夜,讓所有存活的對象向一端移動
7. GC 收集器有哪些匙铡?CMS 收集器與 G1 收集器的特點。
并行收集器:串行收集器使用一個單獨的線程進(jìn)行收集碍粥,GC 時服務(wù)有停頓時間
串行收集器:次要回收中使用多線程來執(zhí)行
CMS 收集器是基于“標(biāo)記—清除”算法實現(xiàn)的鳖眼,經(jīng)過多次標(biāo)記才會被清除
G1 從整體來看是基于“標(biāo)記—整理”算法實現(xiàn)的收集器,從局部(兩個 Region 之間)上來看是基于“復(fù)制”算法實現(xiàn)的
8. Minor GC 與 Full GC 分別在什么時候發(fā)生嚼摩?
新生代內(nèi)存不夠用時候發(fā)生 MGC 也叫 YGC钦讳,JVM 內(nèi)存不夠的時候發(fā)生 FGC
9. 幾種常用的內(nèi)存調(diào)試工具:jmap矿瘦、jstack、jconsole蜂厅、jhat
jstack 可以看當(dāng)前棧的情況匪凡,jmap 查看內(nèi)存,jhat 進(jìn)行 dump 堆的信息mat(eclipse 的也要了解一下)
10. 類加載的幾個過程:
加載掘猿、驗證病游、準(zhǔn)備、解析稠通、初始化衬衬。然后是使用和卸載了
通過全限定名來加載生成 class 對象到內(nèi)存中,然后進(jìn)行驗證這個 class 文件改橘,包括文件格式校驗滋尉、元數(shù)據(jù)驗證,字節(jié)碼校驗等飞主。準(zhǔn)備是對這個對象分配內(nèi)存狮惜。解析是將符號引用轉(zhuǎn)化為直接引用(指針引用),初始化就是開始執(zhí)行構(gòu)造器的代碼
11.JVM 內(nèi)存分哪幾個區(qū)碌识,每個區(qū)的作用是什么?
java 虛擬機主要分為以下一個區(qū):
方法區(qū):
1. 有時候也成為永久代碾篡,在該區(qū)內(nèi)很少發(fā)生垃圾回收,但是并不代表不發(fā)生 GC筏餐,在這里進(jìn)行的 GC 主要是對方法區(qū)里的常量池和對類型的卸載
2. 方法區(qū)主要用來存儲已被虛擬機加載的類的信息开泽、常量、靜態(tài)變量和即時編譯器編譯后的代碼等數(shù)據(jù)魁瞪。
3. 該區(qū)域是被線程共享的穆律。
4. 方法區(qū)里有一個運行時常量池,用于存放靜態(tài)編譯產(chǎn)生的字面量和符號引用导俘。該常量池具有動態(tài)性峦耘,也就是說常量并不一定是編譯時確定,運行時生成的常量也會存在這個常量池中旅薄。
虛擬機棧:
1. 虛擬機棧也就是我們平常所稱的棧內(nèi)存,它為 java 方法服務(wù)贡歧,每個方法在執(zhí)行的時候都會創(chuàng)建一個棧幀,用于存儲局部變量表赋秀、操作數(shù)棧利朵、動態(tài)鏈接和方法出口等信息。
2. 虛擬機棧是線程私有的猎莲,它的生命周期與線程相同绍弟。
3. 局部變量表里存儲的是基本數(shù)據(jù)類型、returnAddress 類型(指向一條字節(jié)碼指令的地址)和對象引用著洼,這個對象引用有可能是指向?qū)ο笃鹗嫉刂返囊粋€指針樟遣,也有可能是代表對象的句柄或者與對象相關(guān)聯(lián)的位置而叼。局部變量所需的內(nèi)存空間在編譯器間確定
4.操作數(shù)棧的作用主要用來存儲運算結(jié)果以及運算的操作數(shù),它不同于局部變量表通過索引來訪問豹悬,而是壓棧和出棧的方式
5.每個棧幀都包含一個指向運行時常量池中該棧幀所屬方法的引用葵陵,持有這個引用是為了支持方法調(diào)用過程中的動態(tài)連接.動態(tài)鏈接就是將常量池中的符號引用在運行期轉(zhuǎn)化為直接引用。
本地方法棧
本地方法棧和虛擬機棧類似瞻佛,只不過本地方法棧為 Native 方法服務(wù)脱篙。
堆
java 堆是所有線程所共享的一塊內(nèi)存,在虛擬機啟動時創(chuàng)建伤柄,幾乎所有的對象實例都在這里創(chuàng)建绊困,因此該區(qū)域經(jīng)常發(fā)生垃圾回收操作。
程序計數(shù)器
內(nèi)存空間小适刀,字節(jié)碼解釋器工作時通過改變這個計數(shù)值可以選取下一條需要執(zhí)行的字節(jié)碼指令秤朗,分支、循環(huán)笔喉、跳轉(zhuǎn)取视、異常處理和線程恢復(fù)等功能都需要依賴這個計數(shù)器完成。該內(nèi)存區(qū)域是唯一一個 java 虛擬機規(guī)范沒有規(guī)定任何 OOM 情況的區(qū)域常挚。
12.如和判斷一個對象是否存活?(或者 GC 對象的判定方法)
判斷一個對象是否存活有兩種方法:
1. 引用計數(shù)法
所謂引用計數(shù)法就是給每一個對象設(shè)置一個引用計數(shù)器作谭,每當(dāng)有一個地方引用這個對像時,就將計數(shù)器加一待侵,引用失效時丢早,計數(shù)器就減一姨裸。當(dāng)一個對象的引用計數(shù)器為零時秧倾,說明此對象沒有被引用,也就是“死對象”,將會被垃圾回收.引用計數(shù)法有一個缺陷就是無法解決循環(huán)引用問題傀缩,也就是說當(dāng)對象 A 引用對象 B那先,對象B 又引用者對象 A,那么此時 A,B 對象的引用計數(shù)器都不為零赡艰,也就造成無法完成垃圾回收售淡,所以主流的虛擬機都沒有采用這種算法。
2.可達(dá)性算法(引用鏈法)
該算法的思想是:從一個被稱為 GC Roots 的對象開始向下搜索慷垮,如果一個對象到 GC Roots 沒有任何引用鏈相連時揖闸,則說明此對象不可用。
在 java 中可以作為 GC Roots 的對象有以下幾種:
虛擬機棧中引用的對象
方法區(qū)類靜態(tài)屬性引用的對象
方法區(qū)常量池引用的對象
本地方法棧 JNI 引用的對象
雖然這些算法可以判定一個對象是否能被回收料身,但是當(dāng)滿足上述條件時汤纸,一個對象比不一定會被回收。當(dāng)一個對象不可達(dá) GC Root 時芹血,這個對象并
不會立馬被回收贮泞,而是出于一個死緩的階段楞慈,若要被真正的回收需要經(jīng)歷兩次標(biāo)記如果對象在可達(dá)性分析中沒有與 GC Root 的引用鏈,那么此時就會被第一次標(biāo)記并且進(jìn)行一次篩選啃擦,篩選的條件是是否有必要執(zhí)行 finalize()方法囊蓝。當(dāng)對象沒有覆蓋 finalize()方法或者已被虛擬機調(diào)用過,那么就認(rèn)為是沒必要的令蛉。
如果該對象有必要執(zhí)行 finalize()方法聚霜,那么這個對象將會放在一個稱為 F-Queue 的對隊列中,虛擬機會觸發(fā)一個 Finalize()線程去執(zhí)行言询,此線程是低優(yōu)先級的俯萎,并且虛擬機不會承諾一直等待它運行完,這是因為如果 finalize()執(zhí)行緩慢或者發(fā)生了死鎖运杭,那么就會造成 FQueue 隊列一直等待夫啊,造成了內(nèi)存回收系統(tǒng)的崩潰。GC 對處于 F-Queue 中的對象進(jìn)行第二次被標(biāo)記辆憔,這時撇眯,該對象將被移除”即將回收”集合,等待回收虱咧。
13.簡述 java 垃圾回收機制?
在 java 中熊榛,程序員是不需要顯示的去釋放一個對象的內(nèi)存的,而是由虛擬機自行執(zhí)行腕巡。在JVM 中玄坦,有一個垃圾回收線程,它是低優(yōu)先級的绘沉,在正常情況下是不會執(zhí)行的煎楣,只有在虛擬機空閑或者當(dāng)前堆內(nèi)存不足時,才會觸發(fā)執(zhí)行车伞,掃面那些沒有被任何引用的對象择懂,并將它們添加到要回收的集合中,進(jìn)行回收另玖。
14.java 中垃圾收集的方法有哪些?
1. 標(biāo)記-清除:
這是垃圾收集算法中最基礎(chǔ)的困曙,根據(jù)名字就可以知道,它的思想就是標(biāo)記哪些要被回收的對象谦去,然后統(tǒng)一回收慷丽。這種方法很簡單,但是會有兩個主要問題:1.效率不高鳄哭,標(biāo)記和清除的效率都很低要糊;2.會產(chǎn)生大量不連續(xù)的內(nèi)存碎片,導(dǎo)致以后程序在分配較大的對象時窃诉,由于沒有充足的連續(xù)內(nèi)存而提前觸發(fā)一次 GC 動作杨耙。
2. 復(fù)制算法:
為了解決效率問題赤套,復(fù)制算法將可用內(nèi)存按容量劃分為相等的兩部分,然后每次只使用其中的一塊珊膜,當(dāng)一塊內(nèi)存用完時容握,就將還存活的對象復(fù)制到第二塊內(nèi)存上,然后一次性清楚完第一塊內(nèi)存车柠,再將第二塊上的對象復(fù)制到第一塊剔氏。但是這種方式,內(nèi)存的代價太高竹祷,每次基本上都要浪費一般的內(nèi)存谈跛。于是將該算法進(jìn)行了改進(jìn),內(nèi)存區(qū)域不再是按照 1:1 去劃分塑陵,而是將內(nèi)存劃分為8:1:1 三部分感憾,較大那份內(nèi)存交 Eden 區(qū),其余是兩塊較小的內(nèi)存區(qū)叫 Survior 區(qū)令花。每次都會優(yōu)先使用 Eden 區(qū)阻桅,若 Eden 區(qū)滿,就將對象復(fù)制到第二塊內(nèi)存區(qū)上兼都,然后清除 Eden 區(qū)嫂沉,如果此時存活的對象太多,以至于 Survivor 不夠時扮碧,會將這些對象通過分配擔(dān)保機制復(fù)制到老年代中趟章。(java 堆又分為新生代和老年代)
3. 標(biāo)記-整理
該算法主要是為了解決標(biāo)記-清除,產(chǎn)生大量內(nèi)存碎片的問題慎王;當(dāng)對象存活率較高時蚓土,也解決了復(fù)制算法的效率問題。它的不同之處就是在清除對象的時候現(xiàn)將可回收對象移動到一端柬祠,然后清除掉端邊界以外的對象北戏,這樣就不會產(chǎn)生內(nèi)存碎片了负芋。
4. 分代收集
現(xiàn)在的虛擬機垃圾收集大多采用這種方式漫蛔,它根據(jù)對象的生存周期,將堆分為新生代和老年代旧蛾。在新生代中莽龟,由于對象生存期短,每次回收都會有大量對象死去锨天,那么這時就采用復(fù)制算法毯盈。老年代里的對象存活率較高,沒有額外的空間進(jìn)行分配擔(dān)保病袄,所以可以使用標(biāo)記-整理 或者 標(biāo)記-清除搂赋。
15.java 內(nèi)存模型
java 內(nèi)存模型(JMM)是線程間通信的控制機制.JMM 定義了主內(nèi)存和線程之間抽象關(guān)系赘阀。線程之間的共享變量存儲在主內(nèi)存(main memory)中,每個線程都有一個私有的本地內(nèi)存(local memory)脑奠,本地內(nèi)存中存儲了該線程以讀/寫共享變量的副本基公。本地內(nèi)存是JMM 的一個抽象概念,并不真實存在宋欺。它涵蓋了緩存轰豆,寫緩沖區(qū),寄存器以及其他的硬件和編譯器優(yōu)化齿诞。Java 內(nèi)存模型的抽象示意圖如下:
從上圖來看酸休,線程 A 與線程 B 之間如要通信的話,必須要經(jīng)歷下面 2 個步驟:
1. 首先祷杈,線程 A 把本地內(nèi)存 A 中更新過的共享變量刷新到主內(nèi)存中去斑司。
2. 然后,線程 B 到主內(nèi)存中去讀取線程 A 之前已更新過的共享變量但汞。
16.java 類加載過程?
類加載的過程
java 類加載需要經(jīng)歷一下 7 個過程(加載陡厘、驗證、準(zhǔn)備特占、解析糙置、初始化、使用是目、卸載):
1.加載
加載時類加載的第一個過程谤饭,在這個階段,將完成一下三件事情:
- 1. 通過一個類的全限定名獲取該類的二進(jìn)制流懊纳。
- 2. 將該二進(jìn)制流中的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法去運行時數(shù)據(jù)結(jié)構(gòu)揉抵。
- 3. 在內(nèi)存中生成該類的 Class 對象,作為該類的數(shù)據(jù)訪問入口嗤疯。
2.驗證
驗證的目的是為了確保 Class 文件的字節(jié)流中的信息不回危害到虛擬機.
在該階段主要完成以下四鐘驗證:
- 1. 文件格式驗證:驗證字節(jié)流是否符合 Class 文件的規(guī)范冤今,如主次版本號是否在當(dāng)前虛擬機范圍內(nèi),常量池中的常量是否有不被支持的類型.
- 2. 元數(shù)據(jù)驗證:對字節(jié)碼描述的信息進(jìn)行語義分析茂缚,如這個類是否有父類戏罢,是否集成了不被繼承的類等。
- 3. 字節(jié)碼驗證:是整個驗證過程中最復(fù)雜的一個階段脚囊,通過驗證數(shù)據(jù)流和控制流的分析龟糕,確定程序語義是否正確,主要針對方法體的驗證悔耘。如:方法中的類型轉(zhuǎn)換是否正確讲岁,跳轉(zhuǎn)指令是否正確等。
- 4. 符號引用驗證:這個動作在后面的解析過程中發(fā)生,主要是為了確保解析動作能正確執(zhí)行缓艳。
3.準(zhǔn)備
準(zhǔn)備階段是為類的靜態(tài)變量分配內(nèi)存并將其初始化為默認(rèn)值校摩,這些內(nèi)存都將在方法區(qū)中進(jìn)行分配。準(zhǔn)備階段不分配類中的實例變量的內(nèi)存阶淘,實例變量將會在對象實例化時隨著對象一起分配在 Java 堆中秧耗。public static int value=123;//在準(zhǔn)備階段 value 初始值為 0 。在初始化階段才會變?yōu)?123 舶治。
4.解析
該階段主要完成符號引用到直接引用的轉(zhuǎn)換動作分井。解析動作并不一定在初始化動作完成之前,也有可能在初始化之后霉猛。
5.初始化
初始化時類加載的最后一步尺锚,前面的類加載過程,除了在加載階段用戶應(yīng)用程序可以通過自定義類加載器參與之外惜浅,其余動作完全由虛擬機主導(dǎo)和控制瘫辩。到了初始化階段,才真正開始執(zhí)行類中定義的 Java 程序代碼坛悉。這些資源有static{}塊伐厌,構(gòu)造函數(shù),父類的初始化等
6.使用:使用過程就是根據(jù)程序定義的行為執(zhí)行
7.卸載:卸載由GC完成裸影。
17挣轨,什么是類加載器,類加載器的幾個類型轩猩?
類加載機制:虛擬機把描述類的數(shù)據(jù)從 Class 文件加載到內(nèi)存卷扮,并對數(shù)據(jù)進(jìn)行校驗,解析和初始化均践,最終形成可以被虛擬機直接使用的 java 類型晤锹。
什么是類加載器:實現(xiàn)通過類的權(quán)限定名獲取該類的二進(jìn)制字節(jié)流的代碼塊叫類加載器
ClassLoader就是用來動態(tài)加載class文件到內(nèi)存當(dāng)中用的。
類加載器類型:
- BootStrapClassLoader:它是最頂層的類加載器彤委,是由C++編寫而成, 已經(jīng)內(nèi)嵌到JVM中了鞭铆。在JVM啟動時會初始化該ClassLoader,它主要用來讀取Java的核心類庫JRE/lib/rt.jar中所有的class文件焦影,這個jar文件中包含了java規(guī)范定義的所有接口及實現(xiàn)车遂。
- ExtensionClassLoader:它是用來讀取Java的一些擴展類庫,如讀取JRE/lib/ext/*.jar中的包等(這里要注意偷办,有些版本的是沒有ext這個目錄的)
- AppClassLoader:它是用來讀取CLASSPATH下指定的所有jar包或目錄的類文件艰额,一般情況下這個就是程序中默認(rèn)的類加載器澄港。
- CustomClassLoader:它是用戶自定義編寫的椒涯,它用來讀取指定類文件 』匚啵基于自定義的ClassLoader可用于加載非Classpath中(如從網(wǎng)絡(luò)上下載的jar或二進(jìn)制)的jar及目錄废岂、還可以在加載前對class文件優(yōu)一些動作祖搓,如解密、編碼等
18湖苞,雙親委托模型
就是判斷該類是否已經(jīng)加載(自身是否已加載過)拯欧,如果沒有則不是自身去查找而是委托給父加載器進(jìn)行查找,這樣依次進(jìn)行遞歸财骨,直到委托到最頂層的Bootstrap ClassLoader,如果Bootstrap ClassLoader找到了該Class,就會直接返回镐作,如果沒找到,則繼續(xù)依次向下查找隆箩,
如果還沒找到則最后交給自身去查找该贾,調(diào)用自身的findClass方法加載
它的好處:
- 避免重復(fù)加載,如果已經(jīng)加載過一次Class捌臊,則不需要再次加載杨蛋,而是直接讀取已經(jīng)加載的Class
- 更加安全,確保理澎,java核心api中定義類型不會被隨意替換逞力,比如,采用雙親委托模式可以使得系統(tǒng)在Java虛擬機啟動時舊加載了String類糠爬,也就無法用自定義的String類來替換系統(tǒng)的String類寇荧,這樣便可以防止核心API庫被隨意篡改。
19执隧,理解類加載器ClassLoader的加載機制
提要:Java程序啟動的時候砚亭,并不會一次性加載程序中所有的.class文件,而是在程序的運行過程中殴玛,動態(tài)地加載相應(yīng)的類到內(nèi)存中捅膘。通常情況下,Java程序中的.class文件會在以下2種情況下被ClassLoader主動加載到內(nèi)存中
調(diào)用類構(gòu)造器 調(diào)用類中的靜態(tài)(static)變量或者靜態(tài)方法
- ClassLoader就是用來加載class文件的,不管是jar中還是dex中的class滚粟。
- Java中的ClassLoader通過雙親委托來加載各自指定路徑下的class
- 可以自定義 ClassLoader寻仗,一般覆蓋 findClass() 方法,不建議重寫 loadClass 方法
- Android 中常用的兩種 ClassLoader 分別為:PathClassLoader 和 DexClassLoader凡壤。
Android相關(guān)拓展:
熱修復(fù)的原理署尤,就是采用classloader機制 將新文件打包成jar包,然后用dx工具將class文件優(yōu)化成dex文件亚侠,然后下載文件到apk里曹体,用DexClassLoader來加載dex文件里的方法來替換錯誤方法
深入分析ClassLoader參考:https://blog.csdn.net/tonytfjing/article/details/47212291
20.簡述 java 內(nèi)存分配與回收策率以及 Minor GC 和Major GC
1. 對象優(yōu)先在堆的 Eden 區(qū)分配。
2. 大對象直接進(jìn)入老年代.
3. 長期存活的對象將直接進(jìn)入老年代.
當(dāng) Eden 區(qū)沒有足夠的空間進(jìn)行分配時硝烂,虛擬機會執(zhí)行一次 Minor GC.Minor Gc 通常發(fā)生在新生代的 Eden 區(qū)箕别,在這個區(qū)的對象生存期短,往往發(fā)生 Gc 的頻率較高,回收速度比較快;Full Gc/Major GC 發(fā)生在老年代串稀,一般情況下除抛,觸發(fā)老年代 GC的時候不會觸發(fā) Minor GC,但是通過配置,可以在 Full GC 之前進(jìn)行一次 MinorGC 這樣可以加快老年代的回收速度母截。
21到忽,導(dǎo)致內(nèi)存泄露的原因有那些?
參考:https://blog.csdn.net/u012516166/article/details/77014910
內(nèi)存泄露的根本原因:長生命周期的對象A持有短生命周期B的對象清寇。B沒有被使用后喘漏,A仍然在引用B,這樣垃圾回收器就無法將B移除內(nèi)存华烟,從而導(dǎo)致內(nèi)心泄露
說白了陷遮,就是該被釋放的對象沒有被釋放,一直被某些實例持有卻不再被使用導(dǎo)致GC不能回收
靜態(tài)內(nèi)部類非靜態(tài)內(nèi)部類的區(qū)別(Handler 引起的內(nèi)存泄漏垦江。)
靜態(tài)集合類引起內(nèi)存泄露
單例模式引起的內(nèi)存泄漏帽馋。
解決:Context是ApplicationContext,由于ApplicationContext的生命周期是和app一致的比吭,不會導(dǎo)致內(nèi)存泄漏
注冊/反注冊未成對使用引起的內(nèi)存泄漏甸陌。
集合對象沒有及時清理引起的內(nèi)存泄漏了讨。通常會把一些對象裝入到集合中,當(dāng)不使用的時候一定要記得及時清理集合,讓相關(guān)對象不再被引用久窟。
內(nèi)存分析工具的使用
減少內(nèi)存對象的占用
I.ArrayMap/SparseArray代替hashmap
II.避免在android里面使用Enum
III.減少bitmap的內(nèi)存占用
inSampleSize:縮放比例革砸,在把圖片載入內(nèi)存之前蔼两,我們需要先計算出一個合適的縮放比例酝枢,避免不必要的大圖載入。
decode format:解碼格式瓢剿,選擇ARGB_8888/RBG_565/ARGB_4444/ALPHA_8逢慌,存在很大差異。
IV.減少資源圖片的大小间狂,過大的圖片可以考慮分段加載
22攻泼,傳統(tǒng)IPC機制的通信原理(2次內(nèi)存拷貝)
1.發(fā)送方進(jìn)程通過系統(tǒng)調(diào)用(copy_from_user)將要發(fā)送的數(shù)據(jù)存拷貝到內(nèi)核緩存區(qū)中。
2.接收方開辟一段內(nèi)存空間鉴象,內(nèi)核通過系統(tǒng)調(diào)用(copy_to_user)將內(nèi)核緩存區(qū)中的數(shù)據(jù)拷貝到接收方的內(nèi)存緩存區(qū)忙菠。
種傳統(tǒng)IPC機制存在2個問題:
1.需要進(jìn)行2次數(shù)據(jù)拷貝,第1次是從發(fā)送方用戶空間拷貝到內(nèi)核緩存區(qū)纺弊,第2次是從內(nèi)核緩存區(qū)拷貝到接收方用戶空間牛欢。
2.接收方進(jìn)程不知道事先要分配多大的空間來接收數(shù)據(jù),可能存在空間上的浪費淆游。