JVM是JAVA虛擬機,將程序文件轉(zhuǎn)變?yōu)?class字節(jié)碼文件,然后通過JVM處理到各個操作系統(tǒng)平臺。
class文件加載過程
加載:查找和導入class文件
驗證:驗證里面的字節(jié)碼文件是否符合要求
準備:給字節(jié)碼文件里面的對象變量分配空間
解析:將符號引用變?yōu)橹苯右?br>
初始化操作
類加載器
類加載器-啟動類加載器-拓展類加載器
雙親委派機制
雙親委派:當一個類收到類加載器加載請求時,它會將這個類加載請求給他的父類加載器,只有當父類加載器不能完成時,子類加載器才會進行類加載。
雙親委派的作用時保證不同類加載器加載的都是同一個對象。
JVM內(nèi)存模型
- 程序計數(shù)器:是非常小的內(nèi)存空間,保存著當前線程JVM指令執(zhí)行的位置。由于JAVA虛擬機多線程是通過線程輪流切換并分配各線程執(zhí)行時間來實現(xiàn)的,因此每個線程都應該有其私有的程序計數(shù)器。該內(nèi)存區(qū)域是JAVA虛擬機規(guī)范中唯一沒有OOM的。
- 虛擬機棧:與程序計數(shù)器一樣,虛擬機棧也是線程私有的,生命周期與線程相同。虛擬機棧是描述JAVA方法執(zhí)行過程的內(nèi)存模型。每個方法被執(zhí)行時都會產(chǎn)生一個棧幀,用來存儲方法局部變量表(包含編譯期間的基本數(shù)據(jù)類型以及對象引用等),方法出口等信息。入棧以及出棧即對應方法的調(diào)用到執(zhí)行完畢的過程。
在JAVA虛擬機規(guī)范中,一個棧對應的棧幀數(shù)量數(shù)固定的,但是每一個棧幀所對應的堆內(nèi)存是可以動態(tài)拓展的。
當棧幀的數(shù)量超過限制是會造成stackoverflow:遞歸調(diào)用
當棧幀對應分配的內(nèi)存太大時會造成outofmemory:在方法執(zhí)行過程中不斷new出來對象且沒有被垃圾回收 - 本地方法棧:與虛擬機棧一樣,只不過本地方法棧是針對native方法。
- 堆內(nèi)存:對大多數(shù)應用來說,堆內(nèi)存是最大的一部分,幾乎所有new出來的對象都要在堆上分配內(nèi)存。是各個線程共享的內(nèi)存區(qū)域。
- 方法區(qū):與堆內(nèi)存一樣,是各個線程共享的內(nèi)存區(qū)域。存儲了類信息,運行時常量池,靜態(tài)變量等。
永久代與元空間
在JAVA8之前方法區(qū)的實現(xiàn)是采用永久代實現(xiàn)的,永久代的垃圾回收與老年代是綁定的,對其中一個進行垃圾回收都會觸發(fā)另外一個的垃圾回收。JAVA8時方法區(qū)采用元空間進行實現(xiàn)。元空間屬于本地內(nèi)存,將永久代中的類信息放在了元空間中,將字符串常量池,靜態(tài)變量放在了堆內(nèi)存中。
為什么要移除永久代
1.字符串存放在永久代中容易產(chǎn)生內(nèi)存溢出問題。
2.對于一些類信息難以確定其大小,對于永久代大小的指定比較困難。
3.永久代與老年代的垃圾回收時綁定的,比較麻煩。
JVM如何判斷一個對象能否被GC,可以被視為root的都有哪幾種類型?
- 引用計數(shù)法。即當對象被引用一次就+1,如果失效了就-1,變成了0之后就可以判斷可以被垃圾回收。但JVM并沒有采用這種方式,因為不能很好的解決循環(huán)利用。比如A與B互相引用,但程序已經(jīng)沒有再引用A和B了。
- 引用鏈法。通過一種GC ROOT對象來進行判斷,GC ROOT代表那種不能夠刪除的對象。如果對象有一條鏈能夠到達該GC ROOT則判斷該對象不能進行回收,反之則可以回收。
可以作為GC ROOT的對象:
System Class:由系統(tǒng)類加載器(System Class Loader)加載的類。自定義類加載器加載的類不一定是 GC ROOT對象。
方法區(qū)中的類靜態(tài)屬性引用對象
方法區(qū)中的常量引用對象
強軟弱虛引用的區(qū)別以及GC對他們執(zhí)行怎樣的操作?
強引用:在程序中普遍存在,一般為直接引用。永遠都不會對其進行垃圾回收。
bject object = new Object();
String str = "hello";
軟引用:用來描述一些還有用但非必須的對象。內(nèi)存不足時會對其進行回收。
弱引用:用來描述非必須對象。會被垃圾回收。
虛引用:虛引用的存在不會對對象的生存時間構(gòu)成任何影響,為一個對象設置虛引用的唯一目的就是能在這個對象被收集器回收時收到一個系統(tǒng)通知。
垃圾回收算法?
1.標記清除算法。從GC ROOT 開始采用引用鏈法標記引用鏈上對象,然后清除未標記的對象。
優(yōu)點:沒有產(chǎn)生額外的內(nèi)存消耗,內(nèi)存利用率高。
缺點:回收的垃圾對象并不一定是連續(xù)的,因此導致存在不連續(xù)的空間,產(chǎn)生內(nèi)存碎片。
2.標記整理算法。開始同樣從GC ROOT開始采用引用鏈法標記引用鏈上的對象,然后將標記的對象移動到內(nèi)存一側(cè)。將剩下的空間回收。
優(yōu)點:解決了標記清除算法產(chǎn)生的內(nèi)存碎片問題。
缺點:效率較低。
3.復制算法。把內(nèi)存空間一分為二,每次只使用其中的一塊。當進行GC時,將存活著的對象復制到另一塊上。然后把已使用過的這一塊內(nèi)存清理。
優(yōu)點:解決內(nèi)存碎片問題,效率較高。
缺點:將內(nèi)存的可用大小壓縮了一半。
** 4.分代回收。詳情見下。**
堆內(nèi)存為什么要分為新生代與老年代?
堆內(nèi)存被分為新生代與老年代。新生代又被細分為Eden和Survivor區(qū),最后Survivor由FromSpace和ToSpace組成。發(fā)生在新生代的GC為Minor GC 。在老年代中的GC則為Major GC。
新生代一般存放很快就要被GC(垃圾回收)的對象,新生代采用的復制算法。在Minor GC時會將還存活的對象放到一個Survivor區(qū)中,然后對Eden與另外一個Survivor進行清理。
老年代一般存放經(jīng)歷了多次GC依然存活的對象。老年代一般采用標記清除或者標記整理算法。
新生代各區(qū)的比例?
Eden-Survivor 8 :1 :1。 對象先放在Eden區(qū)中,當快滿了觸發(fā)Minor GC時。采用復制算法將Eden區(qū)與Survivor中from區(qū)的內(nèi)存復制到to區(qū)。按照這個比例和復制算法可以比較高效的利用內(nèi)存與解決內(nèi)存碎片的問題。
新生代怎么進入老年代?
1.分配擔保機制。當Minor GC時,一個Survivor區(qū)存放不了那么多時就會進入老年代。
2.超過所設置的內(nèi)存大小的對象會進入老年代。
3.當進行一次Minor GC后,還存在的元素就會+1,一般當?shù)剿O置的臨界點時會進入老年代。
垃圾收集器?
https://www.cnblogs.com/chenpt/p/9803298.html
單線程垃圾收集器:采用單線程進行垃圾回收,當在垃圾回收過程中,會暫停所有工作線程,也是就說是一直STW的。
多線程垃圾收集器:采用多個線程進行垃圾回收,提高效率,但是同樣在垃圾回收過程中,其他線程一直是STW。
多線程并發(fā)垃圾收集器:比如CMS垃圾收集器。在進行垃圾回收過程中,其他線程一樣的進行工作,降低了STW時間。
到現(xiàn)在是G1垃圾收集器。
CMS垃圾收集器:
老年代收集器,采用標記-清除算法與多線程并發(fā)的垃圾回收方式降低了STW時間。
垃圾回收過程:
初步標記:標記GC ROOT直接可達對象,這是STW的。耗時較短
并發(fā)標記:垃圾回收線程與其他工作線程一起工作。進行可達性分析,耗時較長
重新標記:標記并發(fā)過程中的可達對象,這也是STW的
并發(fā)清除:與其他線程一起工作清除垃圾。
缺點:
使用標記-清除算法,可能會產(chǎn)生碎片。
不能處理浮動垃圾。因為采用并發(fā)清除 在標記后 清除前 產(chǎn)生的垃圾在這一次垃圾回收過程中不能被回收。
G1收集器:
克服了CMS收集器產(chǎn)生垃圾碎片的缺點,其次他可以精確的控制停頓時間,也就是STW時間。
G1垃圾收集器引入了分區(qū)的思想,弱化了分代的概念。它將內(nèi)存空間分成很多區(qū),將這些區(qū)域分成新生代Eden區(qū)與survivor區(qū),老年代,還有一個區(qū)單獨存放那些內(nèi)存很大的對象,一般是大于二分之一個region區(qū)。G1收集器將對象從一個區(qū)復制到另外的區(qū),避免了內(nèi)存碎片這個問題。然后引入了一個remember set 記錄引用信息,這樣就不必進行整堆掃描了。最后G1垃圾收集器會判斷哪一個區(qū)最具有回收價值,對其進行回收。
初始標記
并發(fā)標記
重新標記
篩選清除:選擇最具有垃圾回收價值的分區(qū)進行回收。
G1垃圾回收器為什么可以控制停頓時間。
因為垃圾回收器在最后進行回收時會對每個分區(qū)進行排序,選擇最具有回收價值的分區(qū)回收。因為其他垃圾收集器都是回收整個垃圾因此時間是不可控的,而G1垃圾收集器進行篩選回收的話,可以設定一定的垃圾回收時間。
ZGC垃圾回收器
JDK11新加入的垃圾收集器,采用動態(tài)Region內(nèi)存布局,使用了讀屏障、染色指針、多重映射等方式實現(xiàn)可并發(fā)的標記-整理算法垃圾收集器。最大的特點是停頓時間短,將停頓時間控制在了10ms內(nèi)??苫厥誘B級的內(nèi)存(4TB)。
- 動態(tài)Region:分為小型region、中型region、大型region存放不同大小的對象。
- 染色指針:和以往的標記算法不同,CMS和G1會在對象的對象頭進行標記,而ZGC采用指針來標記對象。并且在并發(fā)的可達性分析過程中采用三色標記來標記對象是否被收集過。
- 多重映射:將不同的虛擬內(nèi)存地址映射到同一物理地址。
- 并發(fā)標記:遍歷對象圖做可達性分析,與G1,CMS一樣在標記開始與標記結(jié)束階段都會停頓。
- 并發(fā)預備重分配。在這個階段會根據(jù)特定條件判斷需要被垃圾回收的region,將這些region組成重分配集。
- 并發(fā)重分配。把重分配集中的存活對象復制到其他region區(qū),并維護一個forward table來記錄舊對象與新對象之間的轉(zhuǎn)向關系。
- 并發(fā)重映射。修正整個堆中指向所有重分配集中舊對象的引用。
優(yōu)點
低停頓:幾乎所有線程都是并發(fā)的,STW時間很短。
內(nèi)存小:沒有寫屏障,或者remember set表類似的東西。
并發(fā)的標記整理算法,不會產(chǎn)生內(nèi)存碎片。
缺點
浮動垃圾
ZGC目前只在Linux/x64上可用。
解決方案
從根本上還是只能通過引入分代思想。針對某一區(qū)域進行更頻繁更快的垃圾回收。
Minor GC 與Full GC
當eden區(qū)內(nèi)存不足的時會Minor GC Minor GC一直是STW的。
當老年代內(nèi)存不足時會Full GC
當新生代中進入老年代的內(nèi)存很大時可能會Full GC
調(diào)用System.gc Full GC
調(diào)優(yōu)參數(shù)
- Xms:初始堆大小
- Xmx:最大堆大小
- Xmn:新生代大小