1. 垃圾收集器以及新生代、老年代、永久代
1.1 講一下新生代、老年代、永久代的區(qū)別
文章來源:http://www.zghlxwxcb.cn/news/detail-760133.html
- 在 Java 中,堆被劃分成兩個不同的區(qū)域:新生代 ( Young )、老年代 ( Old )。而新生代 ( Young ) 又被劃分為三個區(qū)域:Eden、From Survivor、To Survivor。這樣劃分的目的是為了使 JVM 能夠更好的管理堆內(nèi)存中的對象,包括內(nèi)存的分配以及回收。
- 新生代中一般保存新出現(xiàn)的對象,所以每次垃圾收集時都發(fā)現(xiàn)大批對象死去,只有少量對象存活,便采用了復制算法,只需要付出少量存活對象的復制成本就可以完成收集。
- 老年代中一般保存存活了很久的對象,他們存活率高、沒有額外空間對它進行分配擔保,就必須采用“標記-清理”或者“標記-整理”算法。
- 永久代就是JVM的方法區(qū)。在這里都是放著一些被虛擬機加載的類信息,靜態(tài)變量,常量等數(shù)據(jù)。這個區(qū)中的東西比老年代和新生代更不容易回收。
1.2 Minor GC、Major GC、Full GC是什么
- Minor GC是新生代GC,指的是發(fā)生在新生代的垃圾收集動作。由于java對象大都是朝生夕死的,所以Minor GC非常頻繁,一般回收速度也比較快。(一般采用復制算法回收垃圾)
- Major GC是老年代GC,指的是發(fā)生在老年代的GC,通常執(zhí)行Major GC會連著Minor GC一起執(zhí)行。Major GC的速度要比Minor GC慢的多。(可采用標記清楚法和標記整理法)
- Full GC是清理整個堆空間,包括年輕代和老年代
1.3 Minor GC、Major GC、Full GC區(qū)別及觸發(fā)條件
- Minor GC 觸發(fā)條件一般為:
- eden區(qū)滿時,觸發(fā)MinorGC。即申請一個對象時,發(fā)現(xiàn)eden區(qū)不夠用,則觸發(fā)一次MinorGC。
- 新創(chuàng)建的對象大小 > Eden所??臻g時觸發(fā)Minor GC
- Major GC和Full GC 觸發(fā)條件一般為:
Major GC通常是跟full GC是等價的- 每次晉升到老年代的對象平均大小>老年代剩余空間
- MinorGC后存活的對象超過了老年代剩余空間
- 永久代空間不足
- 執(zhí)行System.gc()
- CMS GC異常
- 堆內(nèi)存分配很大的對象
1.4 為什么新生代要分Eden和兩個 Survivor 區(qū)域?
- 如果沒有Survivor,Eden區(qū)每進行一次Minor GC,存活的對象就會被送到老年代。老年代很快被填滿,觸發(fā)Major GC.老年代的內(nèi)存空間遠大于新生代,進行一次Full GC消耗的時間比Minor GC長得多,所以需要分為Eden和Survivor。
- Survivor的存在意義,就是減少被送到老年代的對象,進而減少Full GC的發(fā)生,Survivor的預篩選保證,只有經(jīng)歷15次Minor GC還能在新生代中存活的對象,才會被送到老年代。
- 設置兩個Survivor區(qū)最大的好處就是解決了碎片化,剛剛新建的對象在Eden中,經(jīng)歷一次Minor GC,Eden中的存活對象就會被移動到第一塊survivor space S0,Eden被清空;等Eden區(qū)再滿了,就再觸發(fā)一次Minor GC,Eden和S0中的存活對象又會被復制送入第二塊survivor space S1(這個過程非常重要,因為這種復制算法保證了S1中來自S0和Eden兩部分的存活對象占用連續(xù)的內(nèi)存空間,避免了碎片化的發(fā)生)
1.5 Java堆老年代( Old ) 和新生代 ( Young ) 的默認比例?
- 默認的新生代 ( Young ) 與老年代 ( Old ) 的比例的值為 1:2 ( 該值可以通過參數(shù) –XX:NewRatio 來指定 ),即:新生代 ( Young ) = 1/3 的堆空間大小。老年代 ( Old ) = 2/3 的堆空間大小。
- 其中,新生代 ( Young ) 被細分為 Eden 和 兩個 Survivor 區(qū)域,Edem 和倆個Survivor 區(qū)域比例是 = 8 : 1 : 1 ( 可以通過參數(shù) –XX:SurvivorRatio 來設定 ),
- 但是JVM 每次只會使用 Eden 和其中的一塊 Survivor 區(qū)域來為對象服務,所以無論什么時候,總是有一塊 Survivor 區(qū)域是空閑著的。
1.6 為什么要這樣分代:
- 其實主要原因就是可以根據(jù)各個年代的特點進行對象分區(qū)存儲,更便于回收,采用最適當?shù)氖占惴ǎ?
- 新生代中,每次垃圾收集時都發(fā)現(xiàn)大批對象死去,只有少量對象存活,便采用了復制算法,只需要付出少量存活對象的復制成本就可以完成收集。
- 而老年代中因為對象存活率高、沒有額外空間對它進行分配擔保,就必須采用“標記-清理”或者“標記-整理”算法。
- 新生代又分為Eden和Survivor (From與To,這里簡稱一個區(qū))兩個區(qū)。加上老年代就這三個區(qū)。數(shù)據(jù)會首先分配到Eden區(qū)當中(當然也有特殊情況,如果是大對象那么會直接放入到老年代(大對象是指需要大量連續(xù)內(nèi)存空間的java對象)。當Eden沒有足夠空間的時候就會觸發(fā)jvm發(fā)起一次Minor GC,。如果對象經(jīng)過一次Minor-GC還存活,并且又能被Survivor空間接受,那么將被移動到Survivor空間當中。并將其年齡設為1,對象在Survivor每熬過一次Minor GC,年齡就加1,當年齡達到一定的程度(默認為15)時,就會被晉升到老年代中了,當然晉升老年代的年齡是可以設置的。
1.7 什么是垃圾回收器他和垃圾算法有什么區(qū)別
- 垃圾收集器是垃圾回收算法(標記清楚法、標記整理法、復制算法、分代算法)的具體實現(xiàn),不同垃圾收集器、不同版本的JVM所提供的垃圾收集器可能會有很在差別。
1.8 說一下 JVM 有哪些垃圾回收器?
- 如果說垃圾收集算法是內(nèi)存回收的方法論,那么垃圾收集器就是內(nèi)存回收的具體實現(xiàn)。下圖展示了7種作用于不同分代的收集器,其中用于回收新生代的收集器包括Serial、PraNew、Parallel Scavenge,回收老年代的收集器包括Serial Old、Parallel Old、CMS,還有用于回收整個Java堆的G1收集器。不同收集器之間的連線表示它們可以搭配使用。
- Serial收集器(復制算法): 新生代單線程收集器,標記和清理都是單線程,優(yōu)點是簡單高效;
- ParNew收集器 (復制算法): 新生代收并行集器,實際上是Serial收集器的多線程版本,在多核CPU環(huán)境下有著比Serial更好的表現(xiàn);
- Parallel Scavenge收集器 (復制算法): 新生代并行收集器,追求高吞吐量,高效利用 CPU。吞吐量 = 用戶線程時間/(用戶線程時間+GC線程時間),高吞吐量可以高效率的利用CPU時間,盡快完成程序的運算任務,適合后臺應用等對交互相應要求不高的場景;
- Serial Old收集器 (標記-整理算法): 老年代單線程收集器,Serial收集器的老年代版本;
- Parallel Old收集器 (標記-整理算法):老年代并行收集器,吞吐量優(yōu)先,Parallel Scavenge收集器的老年代版本;
- CMS(Concurrent Mark Sweep)收集器(標記-清除算法):老年代并行收集器,以獲取最短回收停頓時間為目標的收集器,具有高并發(fā)、低停頓的特點,追求最短GC回收停頓時間。
- G1(Garbage First)收集器 ( 標記整理 + 復制算法來回收垃圾 ):Java堆并行收集器,G1收集器是JDK1.7提供的一個新收集器,G1收集器基于“標記-整理”算法實現(xiàn),也就是說不會產(chǎn)生內(nèi)存碎片。此外,G1收集器不同于之前的收集器的一個重要特點是:G1回收的范圍是整個Java堆(包括新生代,老年代),而前六種收集器回收的范圍僅限于新生代或老年代。
1.9 收集器可以這么分配?(了解就好了)
Serial / Serial Old
Serial / CMS
ParNew / Serial Old
ParNew / CMS
Parallel Scavenge / Serial Old
Parallel Scavenge / Parallel Old
G1
1.10 新生代垃圾回收器和老年代垃圾回收器都有哪些?有什么區(qū)別?
- 新生代回收器:Serial、ParNew、Parallel Scavenge
- 老年代回收器:Serial Old、Parallel Old、CMS
- 整堆回收器:G1
新生代垃圾回收器一般采用的是復制算法,復制算法的優(yōu)點是效率高,缺點是內(nèi)存利用率低;老年代回收器一般采用的是標記-整理的算法進行垃圾回收。文章來源地址http://www.zghlxwxcb.cn/news/detail-760133.html
1.11 簡述分代垃圾回收器是怎么工作的?
- 分代回收器有兩個分區(qū):老生代和新生代,新生代默認的空間占比總空間的 1/3,老生代的默認占比是 2/3。
- 新生代使用的是復制算法,新生代里有 3 個分區(qū):Eden、To Survivor、From Survivor,它們的默認占比是 8:1:1,它的執(zhí)行流程如下:
- 把 Eden + From Survivor 存活的對象放入 To Survivor 區(qū);
- 清空 Eden 和 From Survivor 分區(qū);
- From Survivor 和 To Survivor 分區(qū)交換,F(xiàn)rom Survivor 變 To Survivor,To Survivor 變 From Survivor。
- 每次在 From Survivor 到 To Survivor 移動時都存活的對象,年齡就 +1,當年齡到達 15(默認配置是 15)時,升級為老生代。大對象也會直接進入老生代。
- 老生代當空間占用到達某個值之后就會觸發(fā)全局垃圾收回,一般使用標記整理的執(zhí)行算法。以上這些循環(huán)往復就構成了整個分代垃圾回收的整體執(zhí)行流程。
2. 內(nèi)存分配策略
2.1 簡述java內(nèi)存分配與回收策率以及Minor GC和Major GC
- 所謂自動內(nèi)存管理,最終要解決的也就是內(nèi)存分配和內(nèi)存回收兩個問題。前面我們介紹了內(nèi)存回收,這里我們再來聊聊內(nèi)存分配。
- 對象的內(nèi)存分配通常是在 Java 堆上分配(隨著虛擬機優(yōu)化技術的誕生,某些場景下也會在棧上分配,后面會詳細介紹),對象主要分配在新生代的 Eden 區(qū),如果啟動了本地線程緩沖,將按照線程優(yōu)先在 TLAB 上分配。少數(shù)情況下也會直接在老年代上分配??偟膩碚f分配規(guī)則不是百分百固定的,其細節(jié)取決于哪一種垃圾收集器組合以及虛擬機相關參數(shù)有關,但是虛擬機對于內(nèi)存的分配還是會遵循以下幾種「普世」規(guī)則:
2.1.1 對象優(yōu)先在 Eden 區(qū)分配
- 多數(shù)情況,對象都在新生代 Eden 區(qū)分配。當 Eden 區(qū)分配沒有足夠的空間進行分配時,虛擬機將會發(fā)起一次 Minor GC。如果本次 GC 后還是沒有足夠的空間,則將啟用分配擔保機制在老年代中分配內(nèi)存。
- 這里我們提到 Minor GC,如果你仔細觀察過 GC 日常,通常我們還能從日志中發(fā)現(xiàn) Major GC/Full GC。
- Minor GC 是指發(fā)生在新生代的 GC,因為 Java 對象大多都是朝生夕死,所有 Minor GC 非常頻繁,一般回收速度也非???;
- Major GC/Full GC 是指發(fā)生在老年代的 GC,出現(xiàn)了 Major GC 通常會伴隨至少一次 Minor GC。Major GC 的速度通常會比 Minor GC 慢 10 倍以上。
2.1.2 為什么大對象直接進入老年代
- 所謂大對象是指需要大量連續(xù)內(nèi)存空間的對象,頻繁出現(xiàn)大對象是致命的,會導致在內(nèi)存還有不少空間的情況下提前觸發(fā) GC 以獲取足夠的連續(xù)空間來安置新對象。
- 前面我們介紹過新生代使用的是標記-清除算法來處理垃圾回收的,如果大對象直接在新生代分配就會導致 Eden 區(qū)和兩個 Survivor 區(qū)之間發(fā)生大量的內(nèi)存復制。因此對于大對象都會直接在老年代進行分配。
2.1.3 長期存活對象將進入老年代
- 虛擬機采用分代收集的思想來管理內(nèi)存,那么內(nèi)存回收時就必須判斷哪些對象應該放在新生代,哪些對象應該放在老年代。因此虛擬機給每個對象定義了一個對象年齡的計數(shù)器,如果對象在 Eden 區(qū)出生,并且能夠被 Survivor 容納,將被移動到 Survivor 空間中,這時設置對象年齡為 1。對象在 Survivor 區(qū)中每「熬過」一次 Minor GC 年齡就加 1,當年齡達到一定程度(默認 15) 就會被晉升到老年代。
到了這里,關于Java虛擬機(JVM)垃圾收集器、新生代、老年代、永久代以及內(nèi)存分配策略的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!