1 什么是垃圾回收
垃圾回收(Garbage Collection,GC)狸页,顧名思義就是釋放垃圾占用的空間锨能,防止內(nèi)存泄露。有效的使用可以使用的內(nèi)存芍耘,對內(nèi)存堆中已經(jīng)死亡的或者長時間沒有使用的對象進行清除和回收址遇。
2 哪些空間的垃圾需要回收
程序員們都知道JVM的內(nèi)存結構包括五大區(qū)域:程序計數(shù)器、虛擬機棧斋竞、本地方法區(qū)倔约、堆區(qū)、方法區(qū)坝初。
其中程序計數(shù)器浸剩、虛擬機棧、本地方法區(qū)3個區(qū)域隨線程而生鳄袍、隨線程而滅绢要,因此這幾個區(qū)域的內(nèi)存分配和回收都具備確定性,就不需要過多考慮回收的問題拗小,因為方法結束或者線程結束時重罪,內(nèi)存自然就跟隨著回收了。
而Java堆區(qū)和方法區(qū)則不一樣哀九!這部分內(nèi)存的分配和回收是動態(tài)的剿配,正是垃圾收集器所需關注的部分。
垃圾收集器在對堆區(qū)和方法區(qū)進行回收前阅束,首先要確定這些區(qū)域的對象哪些可以被回收呼胚,哪些暫時還不能回收,這就要用到判斷對象是否存活的算法息裸!
3 如何定義垃圾
3.1 引用計數(shù)算法
引用計數(shù)算法(Reachability Counting)是通過在堆中的每個對象頭中分配一個空間來保存該對象被引用的次數(shù)(Reference Count)蝇更。如果該對象被其它對象引用琢融,則它的引用計數(shù)加1,如果刪除對該對象的引用簿寂,那么它的引用計數(shù)就減1,當該對象的引用計數(shù)為0時宿亡,那么該對象就會被回收常遂。當一個對象實例被垃圾收集時,它引用的任何對象實例的引用計數(shù)減1挽荠。
優(yōu)點:引用計數(shù)算法可以很快的執(zhí)行,因為它是將垃圾回收分攤到整個應用程序的運行當中了圈匆,而不是在進行垃圾收集時漠另,要掛起整個應用的運行,直到對堆中所有對象的處理都結束跃赚。因此笆搓,采用引用計數(shù)的垃圾收集不屬于嚴格意義上的"Stop-The-World"的垃圾收集機制。它對程序需要不被長時間打斷的實時環(huán)境比較有利纬傲。
缺點:無法檢測出循環(huán)引用满败。這樣就導致一些循環(huán)引用的引用計數(shù)不可能為0,導致永遠無法會回收叹括。
什么原因?qū)е挛覀冏罱K放棄了引用計數(shù)算法呢算墨?
如下面的代碼,定義2個對象汁雷,相互引用净嘀,然后置空各自的聲明引用,最后這2個對象已經(jīng)不可能再被訪問了侠讯, 但由于他們相互引用著對方挖藏,導致它們的引用計數(shù)永遠都不會為0,通過引用計數(shù)算法厢漩,也就永遠無法通知GC收集器回收它們熬苍。
3.2 可達性分析算法
可達性分析算法(Reachability Analysis)的基本思路是,通過一些被稱為引用鏈(GC Roots)的對象作為起點袁翁,從這些節(jié)點開始向下尋找對應的引用節(jié)點(Reference Chain)柴底,找到這個節(jié)點后以后,繼續(xù)尋找這個節(jié)點的引用節(jié)點粱胜,當所有的引用節(jié)點尋找完畢之后柄驻,剩余的節(jié)點則被認為是沒有被引用的節(jié)點,即無用的節(jié)點焙压,無用的節(jié)點將被會判定為是可回收的對象鸿脓。
通過可達性算法抑钟,成功解決了引用計數(shù)所無法解決的問題-“循環(huán)依賴”,只要你無法與 GC Root 建立直接或間接的連接野哭,系統(tǒng)就會判定你為可回收對象在塔。那這樣就引申出了另一個問題,哪些屬于 GC Root拨黔。
3.3 Java內(nèi)存區(qū)域
在 Java 語言中蛔溃,可作為 GC Root 的對象包括以下4種:
虛擬機棧中引用的對象(棧幀中的本地變量表)
方法區(qū)中類靜態(tài)屬性引用的對象
方法區(qū)中常量引用的對象
本地方法棧中 JNI(即一般說的 Native 方法)引用的對象
(1) 虛擬機棧中引用的對象(棧幀中的本地變量表)
此時的 s,即為 GC Root篱蝇,當s置空時贺待,localParameter 對象也斷掉了與 GC Root 的引用鏈,將被回收零截。
(2)方法區(qū)中類靜態(tài)屬性引用的對象
s 為 GC Root麸塞,s 置為 null,經(jīng)過 GC 后涧衙,s 所指向的 properties 對象由于無法與 GC Root 建立關系被回收哪工。
而 m 作為類的靜態(tài)屬性,也屬于 GC Root弧哎,parameter 對象依然與 GC root 建立著連接正勒,所以此時 parameter 對象并不會被回收。
(3) 方法區(qū)中常量引用的對象
m 即為方法區(qū)中的常量引用傻铣,也為 GC Root章贞,s 置為 null 后,final 對象也不會因沒有與 GC Root 建立聯(lián)系而被回收非洲。
(4)本地方法棧中引用的對象
任何 native 接口都會使用某種本地方法棧鸭限,實現(xiàn)的本地方法接口是使用 C 連接模型的話,那么它的本地方法棧就是 C 棧两踏。當線程調(diào)用 Java 方法時败京,虛擬機會創(chuàng)建一個新的棧幀并壓入 Java 棧。然而當它調(diào)用的是本地方法時梦染,虛擬機會保持 Java 棧不變赡麦,不再在線程的 Java 棧中壓入新的幀,虛擬機只是簡單地動態(tài)連接并直接調(diào)用指定的本地方法帕识。
3.4 Java中的引用你了解多少
無論是通過引用計數(shù)算法判斷對象的引用數(shù)量泛粹,還是通過可達性分析算法判斷對象的引用鏈是否可達,判定對象是否存活都與“引用”有關肮疗。在Java語言中晶姊,將引用又分為強引用、軟引用伪货、弱引用们衙、虛引用4種钾怔,這四種引用強度依次逐漸減弱。
- 強引用
在程序代碼中我們使用最普遍的就是強引用蒙挑,類似 Object obj = new Object() 這類引用宗侦。如果一個對象具有強引用,只要強引用還存在忆蚀,那就類似于必不可少的生活用品矾利,垃圾收集器絕不會回收它。
當內(nèi)存不足時蜓谋,Java虛擬機寧愿拋出OutOfMemoryError錯誤,使程序異常終止炭分,也不會靠隨意回收具有強引用的對象來解決內(nèi)存不足的問題桃焕。
在ArrayList類中定義了一個私有的變量elementData數(shù)組,在調(diào)用方法清空數(shù)組時可以看到為每個數(shù)組內(nèi)容賦值為null捧毛。不同于elementData=null观堂,強引用仍然存在,避免在后續(xù)調(diào)用 add()等方法添加元素時進行重新的內(nèi)存分配呀忧。使用如clear()方法中釋放內(nèi)存的方法對數(shù)組中存放的引用類型特別適用师痕,這樣就可以及時釋放內(nèi)存。
- 軟引用(SoftReference)
用來描述一些還有用但并非必須的對象而账,就類似于可有可無的生活用品胰坟。對于軟引用關聯(lián)著的對象,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前(內(nèi)存空間不足時)泞辐,將會把這些對象列進回收范圍之中進行第二次回收笔横。如果這次回收后還沒有足夠的內(nèi)存,才會拋出內(nèi)存溢出異常咐吼。
軟引用可用來實現(xiàn)內(nèi)存敏感的高速緩存吹缔。在實際中有重要的應用,例如瀏覽器的后退按鈕锯茄。
按后退時厢塘,這個后退時顯示的網(wǎng)頁內(nèi)容是重新進行請求還是從緩存中取出呢?這就要看具體的實現(xiàn)策略了肌幽。
(1)如果一個網(wǎng)頁在瀏覽結束時就進行內(nèi)容的回收晚碾,則按后退查看前面瀏覽過的頁面時,需要重新構建
(2)如果將瀏覽過的網(wǎng)頁存儲到內(nèi)存中會造成內(nèi)存的大量浪費喂急,甚至會造成內(nèi)存溢出
這時候就可以使用軟引用迄薄。
- 弱引用(WeakReference)
弱引用是用來描述非必需對象的,那就類似于可有可無的生活用品煮岁。弱引用與軟引用的區(qū)別在于:具有弱引用的對象擁有更短暫的生命周期讥蔽。它的強度比軟引用更弱一些涣易,被弱引用關聯(lián)的對象只能生存到下一次垃圾收集發(fā)生之前。當垃圾收集器工作時冶伞,無論當前內(nèi)存是否足夠新症,都會回收掉只被弱引用關聯(lián)的對象枪萄。不過问慎,由于垃圾回收器是一個優(yōu)先級很低的線程酌壕,因此不一定會很快發(fā)現(xiàn)那些只具有弱引用的對象妇菱。
如果一個對象只是偶爾使用,并且希望在使用時隨時就能獲取到,但又不想影響此對象的垃圾收集汪诉,那么就可以用弱引用來記住此對象长踊。
- 虛引用(PhantomReference)
虛引用就是形同虛設棚潦,與其他幾種引用都不同儡率。如果一個對象僅持有虛引用侯繁,那么它和沒有任何引用一樣胖喳,在任何時候都可能被垃圾回收。它的作用是能在這個對象被收集器回收時收到一個系統(tǒng)通知贮竟。
特別注意丽焊,在實際程序設計中一般很少使用弱引用和虛引用,使用軟引用的情況較多咕别,這是因為軟引用可以加速JVM對垃圾內(nèi)存的回收速度技健,可以維護系統(tǒng)的運行安全,防止內(nèi)存溢出等問題的產(chǎn)生惰拱。
特別說明:無論引用計數(shù)算法還是可達性分析算法都是基于強引用而言的雌贱。
3.5 對象死亡(被回收)前的最后一次掙扎
即使在可達性分析算法中不可達的對象,也并非是“非死不可”偿短,這時候它們暫時處于“緩刑”階段帽芽,要真正宣告一個對象死亡,至少要經(jīng)歷兩次標記過程翔冀。
第一次標記:如果對象在進行可達性分析后發(fā)現(xiàn)沒有與GC Roots相連接的引用鏈导街,那它將會被第一次標記;
第二次標記:第一次標記后接著會進行一次篩選纤子,篩選的條件是此對象是否有必要執(zhí)行finalize()方法搬瑰。在finalize()方法中沒有重新與引用鏈建立關聯(lián)關系的,將被進行第二次標記控硼。
第二次標記成功的對象將真的會被回收泽论,如果對象在finalize()方法中重新與引用鏈建立了關聯(lián)關系,那么將會逃離本次回收卡乾,繼續(xù)存活翼悴。
3.6 方法區(qū)如何判斷是否需要回收
方法區(qū)存儲內(nèi)容是否需要回收的判斷和堆中是不一樣的。方法區(qū)主要回收的內(nèi)容有:廢棄常量和無用的類幔妨。對于廢棄常量也可通過引用的可達性來判斷鹦赎,但是對于無用的類則需要同時滿足下面3個條件:
該類所有的實例都已經(jīng)被回收谍椅,也就是Java堆中不存在該類的任何實例;
加載該類的ClassLoader已經(jīng)被回收古话;
該類對應的java.lang.Class對象沒有在任何地方被引用雏吭,無法在任何地方通過反射訪問該類的方法。
關于類加載的原理陪踩,也是阿里面試的主角杖们,面試官也問過比如:能否自己定義String,答案是不行肩狂,因為jvm在加載類的時候會執(zhí)行雙親委派摘完。
4 怎么回收垃圾
下面討論幾種常見的垃圾收集算法的核心思想。
4.1 標記-清除算法(Mark-Sweep)
標記清除算法(Mark-Sweep)是最基礎的一種垃圾回收算法傻谁,它分為2部分孝治,從根集合(GC Roots)進行掃描,對存活的對象進行標記栅螟,標記完畢后荆秦,再掃描整個空間中未被標記的對象篱竭,如上圖所示力图,標記-清除算法不需要進行對象的移動,只需對不存活的對象進行處理掺逼,在存活對象較多的情況下極為高效吃媒,但由于標記-清除算法直接回收不存活的對象,因此會造成內(nèi)存碎片吕喘。
我們知道開辟內(nèi)存空間時赘那,需要的是連續(xù)的內(nèi)存區(qū)域,如果內(nèi)存碎片都是1M大小的話氯质,這時候我們?nèi)粜枰粋€ 2M的內(nèi)存區(qū)域募舟,其中有2個 1M 是沒法用的。這樣就導致闻察,其實我們本身還有這么多的內(nèi)存的拱礁,但卻用不了。
4.2 復制算法(Copying)
復制算法(Copying)是在標記清除算法上演化而來辕漂,解決標記清除算法的內(nèi)存碎片問題呢灶。它開始時把堆分成一個對象面和多個空閑面, 程序從對象面為對象分配空間钉嘹,當對象面滿了鸯乃,基于copying算法的垃圾收集就從根集合(GC Roots)中掃描活動對象,并將每個活動對象復制到空閑面(使得活動對象所占的內(nèi)存之間沒有空閑洞)跋涣,這樣空閑面變成了對象面缨睡,原來的對象面變成了空閑面鸟悴,程序會在新的對象面中分配內(nèi)存。
這樣就保證了內(nèi)存空間的連續(xù)可用宏蛉,內(nèi)存分配時也就不用考慮內(nèi)存碎片等復雜情況遣臼,邏輯清晰,運行高效拾并。然而很明顯暴露了另一個問題揍堰,空間浪費,代價實在太高嗅义。
4.3 標記-整理算法(Mark-Compact)
標記整理算法(Mark-Compact)標記過程仍然與標記清除算法一樣屏歹,但后續(xù)步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動之碗,并更新對應的空閑指針蝙眶,然后再清理掉端指針邊界以外的內(nèi)存區(qū)域。
標記整理算法一方面在標記-清除算法上做了升級褪那,解決了內(nèi)存碎片的問題幽纷,也規(guī)避了復制算法只能利用一半內(nèi)存區(qū)域的弊端〔┚矗看起來很美好友浸,但從上圖可以看到,它對內(nèi)存變動更頻繁偏窝,需要整理所有存活對象的引用地址收恢,在效率上比復制算法要差很多。
4.4 分代收集算法
分代收集算法(Generational Collection)嚴格來說并不是一種思想或理論祭往,而是融合上述3種基礎的算法思想伦意,而產(chǎn)生的針對不同情況所采用不同算法的一套組合拳。
對象存活周期的不同將內(nèi)存劃分為幾塊硼补,一般是把 Java 堆分為新生代和老年代驮肉,這樣就可以根據(jù)各個年代的特點采用最適當?shù)氖占惴āT谛律幸押В看卫占瘯r都發(fā)現(xiàn)有大批對象死去离钝,只有少量存活,那就選用復制算法疾捍,只需要付出少量存活對象的復制成本就可以完成收集奈辰。而老年代中因為對象存活率高、沒有額外空間對它進行分配擔保乱豆,就必須使用標記-清理或者標記 - 整理算法來進行回收奖恰。
so,另一個問題來了,那內(nèi)存區(qū)域到底被分為哪幾塊瑟啃,每一塊又有什么特別適合什么算法呢论泛?
4.5 內(nèi)存模型與回收策略
Java 堆(Java Heap)是JVM所管理的內(nèi)存中最大的一塊,堆又是垃圾收集器管理的主要區(qū)域蛹屿,這里我們主要分析一下 Java 堆的結構屁奏。
Java堆主要分為2個區(qū)域:新生代與老年代,其中新生代內(nèi)存按照8:1:1的比例又分為 Eden 區(qū)和 兩個Survivor區(qū)( From 和 To 2個區(qū))错负。
可能這時候大家會有疑問坟瓢,為什么要分為新生代與老年代呢?而新生代為什么又需要Survivor 區(qū)犹撒,為什么Survivor區(qū)還要再分2個區(qū)呢折联。別急,下面咱就絮叨絮叨识颊。
4.5.1 新生代的回收算法(回收以Copying復制算法為主)
所有新生成的對象首先都是放在新生代的诚镰,新生代的目標就是盡可能的快速收集那些生命周期短的對象。
大多數(shù)情況下祥款,對象會在新生代 Eden 區(qū)中進行分配清笨,當Eden區(qū)沒有足夠空間進行分配時,虛擬機會發(fā)起一次Minor GC刃跛,Minor GC 相比 Major GC 更頻繁抠艾,回收速度也更快。
進行Minor GC時奠伪,會將Eden區(qū)無需回收的對象復制到Survivor的From區(qū)(若From區(qū)不夠跌帐,則直接進入Old區(qū))首懈,然后清空Eden區(qū)绊率。當From區(qū)也存放滿了時,會將Eden區(qū)和From存活的對象放到Survivor的To區(qū)究履,然后清空Eden區(qū)和Survivor的From區(qū)滤否。此時Survivor的From區(qū)是空的,然后將Survivor的From區(qū)和To區(qū)交換最仑,即保持Survivor的To區(qū)為空藐俺,如此往復。
當Survivor的To區(qū)空間不夠泥彤,不足以存放Eden 區(qū)和 From 存活的對象事欲芹,就會將存活對象直接存放到 老年代(Old 區(qū))。
新生代發(fā)生的GC也叫做Minor GC吟吝,MinorGC發(fā)生頻率比較高(不一定等Eden區(qū)滿了才觸發(fā))菱父。
(1)為啥需要Survivor區(qū)?
不就是新生代到老年代么,直接 Eden 到 Old 不好了嗎浙宜,為啥要這么復雜官辽。想想如果沒有 Survivor 區(qū),Eden 區(qū)每進行一次 Minor GC粟瞬,存活的對象就會被送到老年代同仆,老年代很快就會被填滿。而有很多對象雖然一次 Minor GC 沒有消滅裙品,但其實也并不會蹦跶多久俗批,或許第二次,第三次就需要被清除市怎。這時候移入老年區(qū)扶镀,很明顯不是一個明智的決定。
所以焰轻,Survivor 區(qū)相當于是 Eden 區(qū)和 Old 區(qū)的一個緩沖臭觉,類似于我們交通燈中的黃燈。它存在意義就是減少被送到老年代的對象辱志,進而減少 Major GC 的發(fā)生蝠筑。Survivor 的預篩選保證,只有經(jīng)歷16次 Minor GC 還能在新生代中存活的對象揩懒,才會被送到老年代什乙。
(2)為啥Survivor需要兩個區(qū)?
設置兩個Survivor區(qū)最大的好處就是解決內(nèi)存碎片化已球。
我們先假設一下臣镣,Survivor如果只有一個區(qū)域會怎樣。Minor GC執(zhí)行后智亮,Eden區(qū)被清空了忆某,存活的對象放到了Survivor區(qū),而之前Survivor區(qū)中的對象阔蛉,可能也有一些是需要被清除的弃舒。
問題來了,這時候我們怎么清除它們状原?在這種場景下聋呢,我們只能標記清除,而我們知道標記清除最大的問題就是內(nèi)存碎片颠区,在新生代這種經(jīng)常會消亡的區(qū)域削锰,采用標記清除必然會讓內(nèi)存產(chǎn)生嚴重的碎片化。
因為Survivor有2個區(qū)域毕莱,所以每次Minor GC器贩,會將之前Eden區(qū)和From區(qū)中的存活對象復制到To區(qū)域测暗。第二次Minor GC時,F(xiàn)rom與To職責兌換磨澡,這時候會將 Eden區(qū)和To區(qū)中的存活對象再復制到From區(qū)域碗啄,以此反復。(職責會互換)
這種機制最大的好處就是稳摄,整個過程中稚字,永遠有一個Survivor space是空的,另一個非空的Survivor space是無碎片的厦酬。那么胆描,Survivor為什么不分更多塊呢?比方說分成三個仗阅、四個昌讲、五個?顯然,如果Survivor區(qū)再細分下去减噪,每一塊的空間就會比較小短绸,容易導致Survivor區(qū)滿,兩塊Survivor區(qū)可能是經(jīng)過權衡之后的最佳方案筹裕。
4.5.2 老年代的回收算法(回收以標記-整理算法為主)
老年代占據(jù)著2/3的堆內(nèi)存空間醋闭,只有在 Major GC 的時候才會進行清理,每次 GC 都會觸發(fā)“Stop-The-World”朝卒。內(nèi)存越大证逻,STW 的時間也越長,所以內(nèi)存也不僅僅是越大就越好抗斤。由于復制算法在對象存活率較高的老年代會進行很多次的復制操作囚企,效率很低,所以老年代這里采用的是標記-整理算法瑞眼。
除了上述所說龙宏,在內(nèi)存擔保機制下,無法安置的對象會直接進到老年代负拟,以下幾種情況也會進入老年代烦衣。
(1)大對象:大對象指需要大量連續(xù)內(nèi)存空間的對象歹河,這部分對象不管是不是“朝生夕死”掩浙,都會直接進到老年代。這樣做主要是為了避免在 Eden 區(qū)及2個 Survivor 區(qū)之間發(fā)生大量的內(nèi)存復制秸歧。當你的系統(tǒng)有非常多“朝生夕死”的大對象時厨姚,得注意了。
(2)長期存活對象: 虛擬機給每個對象定義了一個對象年齡(Age)計數(shù)器键菱。正常情況下對象會不斷的在 Survivor 的 From 區(qū)與 To 區(qū)之間移動谬墙,對象在 Survivor 區(qū)中沒經(jīng)歷一次 Minor GC今布,年齡就增加1歲。當年齡增加到15歲時拭抬,這時候就會被轉移到老年代部默。當然,這里的15造虎,JVM 也支持進行特殊設置傅蹂。
(3)動態(tài)對象年齡: 虛擬機并不重視要求對象年齡必須到15歲,才會放入老年區(qū)算凿,如果 Survivor 空間中相同年齡所有對象大小的總合大于 Survivor 空間的一半份蝴,年齡大于等于該年齡的對象就可以直接進去老年區(qū),無需等你“成年”氓轰。這其實有點類似于負載均衡婚夫,輪詢是負載均衡的一種,保證每臺機器都分得同樣的請求署鸡“覆冢看似很均衡,但每臺機的硬件不通靴庆,健康狀況不同侍筛,我們還可以基于每臺機接受的請求數(shù),或每臺機的響應時間等撒穷,來調(diào)整我們的負載均衡算法匣椰。
5 GC是什么時候觸發(fā)的
GC分為兩種:Major GC(或稱為Full GC)和minor GC,老年代采用標記-整理算法的Major GC端礼,新生代采用復制算法的minor GC禽笑。新生代是GC收集垃圾的頻繁區(qū)域。
在最近幾個版本的JDK里默認包括了對永生帶即方法區(qū)的回收(JDK8中無永生帶了)蛤奥,出現(xiàn)Full GC的時候經(jīng)常伴隨至少一次的Minor GC,但非絕對的佳镜。Major GC的速度一般會比Minor GC慢10倍以上。下邊看看有那種情況觸發(fā)JVM進行Full GC及應對策略凡桥。
Minor GC觸發(fā)條件:
一般情況下蟀伸,當新對象生成,并且在Eden區(qū)申請空間失敗時缅刽,就會觸發(fā)觸發(fā)Minor GC啊掏。
Full GC觸發(fā)條件:
(1)System.gc()方法的調(diào)用
此方法的調(diào)用是建議JVM進行Full GC,雖然只是建議而非一定,但很多情況下它會觸發(fā) Full GC,從而增加Full GC的頻率,也即增加了間歇性停頓的次數(shù)。強烈影響系建議能不使用此方法就別使用衰猛,讓虛擬機自己去管理它的內(nèi)存迟蜜,可通過通過-XX:+ DisableExplicitGC來禁止RMI(Java遠程方法調(diào)用)調(diào)用System.gc。
(2)老年代空間不足
老年代空間只有在新生代對象轉入及創(chuàng)建為大對象啡省、大數(shù)組時才會出現(xiàn)不足的現(xiàn)象娜睛,當執(zhí)行Full GC后空間仍然不足髓霞,則拋出如下錯誤: java.lang.OutOfMemoryError: Java heap space為避免以上兩種狀況引起的Full GC,調(diào)優(yōu)時應盡量做到讓對象在Minor GC階段被回收畦戒、讓對象在新生代多存活一段時間及不要創(chuàng)建過大的對象及數(shù)組方库。
(3)方法區(qū)空間不足
JVM規(guī)范中運行時數(shù)據(jù)區(qū)域中的方法區(qū),在HotSpot虛擬機中又被習慣稱為永生代或者永生區(qū)障斋,Permanet Generation中存放的為一些class的信息薪捍、常量、靜態(tài)變量等數(shù)據(jù)配喳,當系統(tǒng)中要加載的類酪穿、反射的類和調(diào)用的方法較多時,Permanet Generation可能會被占滿晴裹,在未配置為采用CMS GC的情況下也會執(zhí)行Full GC被济。如果經(jīng)過Full GC仍然回收不了,那么JVM會拋出如下錯誤信息:java.lang.OutOfMemoryError: PermGen space
涧团。
為避免Perm Gen占滿造成Full GC現(xiàn)象只磷,可采用的方法為增大Perm Gen空間或轉為使用CMS GC。
(4)通過Minor GC后進入老年代的平均大小大于老年代的可用內(nèi)存
如果發(fā)現(xiàn)統(tǒng)計數(shù)據(jù)說之前Minor GC的平均晉升大小比目前old gen剩余的空間大泌绣,則不會觸發(fā)Minor GC而是轉為觸發(fā)full GC