您好,登錄后才能下訂單哦!
Java的性能優化,JVM GC(垃圾回收機制)在學習Java GC 之前,我們需要記住一個單詞:stop-the-world 。它會在任何一種GC算法中發生。stop-the-world 意味著JVM因為需要執行GC而停止了應用程序的執行。當stop-the-world 發生時,除GC所需的線程外,所有的線程都進入等待狀態,直到GC任務完成。GC優化很多時候就是減少stop-the-world 的發生。
JVM GC回收哪個區域內的垃圾? 需要注意的是,JVM GC只回收堆區和方法區內的對象。而棧區的數據,在超出作用域后會被JVM自動釋放掉,所以其不在JVM GC的管理范圍內。
JVM GC怎么判斷對象可以被回收了? · 對象沒有引用 · 作用域發生未捕獲異常 · 程序在作用域正常執行完畢 · 程序執行了System.exit() · 程序發生意外終止(被殺線程等) 在Java程序中不能顯式的分配和注銷緩存,因為這些事情JVM都幫我們做了,那就是GC。 有些時候我們可以將相關的對象設置成null 來試圖顯示的清除緩存,但是并不是設置為null 就會一定被標記為可回收,有可能會發生逃逸。 將對象設置成null 至少沒有什么壞處,但是使用System.gc() 便不可取了,使用System.gc() 時候并不是馬上執行GC操作,而是會等待一段時間,甚至不執行,而且System.gc() 如果被執行,會觸發Full GC ,這非常影響性能。
JVM GC什么時候執行? eden區空間不夠存放新對象的時候,執行Minro GC。升到老年代的對象大于老年代剩余空間的時候執行Full GC,或者小于的時候被HandlePromotionFailure 參數強制Full GC 。調優主要是減少 Full GC 的觸發次數,可以通過 NewRatio 控制新生代轉老年代的比例,通過MaxTenuringThreshold 設置對象進入老年代的年齡閥值(后面會介紹到)。
按代的垃圾回收機制 新生代(Young generation):絕大多數最新被創建的對象都會被分配到這里,由于大部分在創建后很快變得不可達,很多對象被創建在新生代,然后“消失”。對象從這個區域“消失”的過程我們稱之為:Minor GC 。 老年代(Old generation):對象沒有變得不可達,并且從新生代周期中存活了下來,會被拷貝到這里。其區域分配的空間要比新生代多。也正由于其相對大的空間,發生在老年代的GC次數要比新生代少得多。對象從老年代中消失的過程,稱之為:Major GC 或者 Full GC。 持久代(Permanent generation)也稱之為 方法區(Method area):用于保存類常量以及字符串常量。注意,這個區域不是用于存儲那些從老年代存活下來的對象,這個區域也可能發生GC。發生在這個區域的GC事件也被算為 Major GC 。只不過在這個區域發生GC的條件非常嚴苛,必須符合以下三種條件才會被回收: 1、所有實例被回收 2、加載該類的ClassLoader 被回收 3、Class 對象無法通過任何途徑訪問(包括反射) 可能我們會有疑問: 如果老年代的對象需要引用新生代的對象,會發生什么呢? 為了解決這個問題,老年代中存在一個 card table ,它是一個512byte大小的塊。所有老年代的對象指向新生代對象的引用都會被記錄在這個表中。當針對新生代執行GC的時候,只需要查詢 card table 來決定是否可以被回收,而不用查詢整個老年代。這個 card table 由一個write barrier 來管理。write barrier給GC帶來了很大的性能提升,雖然由此可能帶來一些開銷,但完全是值得的。
默認的新生代(Young generation)、老年代(Old generation)所占空間比例為 1 : 2 。
新生代空間的構成與邏輯 為了更好的理解GC,我們來學習新生代的構成,它用來保存那些第一次被創建的對象,它被分成三個空間: · 一個伊甸園空間(Eden) · 兩個幸存者空間(Fron Survivor、To Survivor) 默認新生代空間的分配:Eden : Fron : To = 8 : 1 : 1
每個空間的執行順序如下: 1、絕大多數剛剛被創建的對象會存放在伊甸園空間(Eden)。 2、在伊甸園空間執行第一次GC(Minor GC)之后,存活的對象被移動到其中一個幸存者空間(Survivor)。 3、此后,每次伊甸園空間執行GC后,存活的對象會被堆積在同一個幸存者空間。 4、當一個幸存者空間飽和,還在存活的對象會被移動到另一個幸存者空間。然后會清空已經飽和的哪個幸存者空間。 5、在以上步驟中重復N次(N = MaxTenuringThreshold(年齡閥值設定,默認15))依然存活的對象,就會被移動到老年代。 從上面的步驟可以發現,兩個幸存者空間,必須有一個是保持空的。如果兩個兩個幸存者空間都有數據,或兩個空間都是空的,那一定是你的系統出現了某種錯誤。 我們需要重點記住的是,對象在剛剛被創建之后,是保存在伊甸園空間的(Eden)。那些長期存活的對象會經由幸存者空間(Survivor)轉存到老年代空間(Old generation)。 也有例外出現,對于一些比較大的對象(需要分配一塊比較大的連續內存空間)則直接進入到老年代。一般在Survivor 空間不足的情況下發生。
老年代空間的構成與邏輯 老年代空間的構成其實很簡單,它不像新生代空間那樣劃分為幾個區域,它只有一個區域,里面存儲的對象并不像新生代空間絕大部分都是朝聞道,夕死矣。這里的對象幾乎都是從Survivor 空間中熬過來的,它們絕不會輕易的狗帶。因此,Full GC(Major GC)發生的次數不會有Minor GC 那么頻繁,并且做一次Major GC 的時間比Minor GC 要更長(約10倍)。
JVM GC 算法講解 1、根搜索算法 根搜索算法是從離散數學中的圖論引入的,程序把所有引用關系看作一張圖,從一個節點GC ROOT 開始,尋找對應的引用節點,找到這個節點后,繼續尋找這個節點的引用節點。當所有的引用節點尋找完畢后,剩余的節點則被認為是沒有被引用到的節點,即無用的節點。
上圖紅色為無用的節點,可以被回收。 目前Java中可以作為GC ROOT的對象有:
1、虛擬機棧中引用的對象(本地變量表)
2、方法區中靜態屬性引用的對象
3、方法區中常量引用的對象
4、本地方法棧中引用的對象(Native對象) 基本所有GC算法都引用根搜索算法這種概念。
2、標記 - 清除算法 標記-清除算法采用從根集合進行掃描,對存活的對象進行標記,標記完畢后,再掃描整個空間中未被標記的對象進行直接回收,如上圖。 標記-清除算法不需要進行對象的移動,并且僅對不存活的對象進行處理,在存活的對象比較多的情況下極為高效,但由于標記-清除算法直接回收不存活的對象,并沒有對還存活的對象進行整理,因此會導致內存碎片。
3、復制算法
復制算法將內存劃分為兩個區間,使用此算法時,所有動態分配的對象都只能分配在其中一個區間(活動區間),而另外一個區間(空間區間)則是空閑的。
復制算法采用從根集合掃描,將存活的對象復制到空閑區間,當掃描完畢活動區間后,會的將活動區間一次性全部回收。此時原本的空閑區間變成了活動區間。下次GC時候又會重復剛才的操作,以此循環。 復制算法在存活對象比較少的時候,極為高效,但是帶來的成本是犧牲一半的內存空間用于進行對象的移動。所以復制算法的使用場景,必須是對象的存活率非常低才行,而且最重要的是,我們需要克服50%內存的浪費。
4、標記 - 整理算法
標記-整理算法采用 標記-清除 算法一樣的方式進行對象的標記、清除,但在回收不存活的對象占用的空間后,會將所有存活的對象往左端空閑空間移動,并更新對應的指針。標記-整理 算法是在標記-清除 算法之上,又進行了對象的移動排序整理,因此成本更高,但卻解決了內存碎片的問題。
JVM為了優化內存的回收,使用了分代回收的方式,對于新生代內存的回收(Minor GC)主要采用復制算法。而對于老年代的回收(Major GC),大多采用標記-整理算法。
垃圾回收器簡介 需要注意的是,每一個回收器都存在Stop The World 的問題,只不過各個回收器在Stop The World 時間優化程度、算法的不同,可根據自身需求選擇適合的回收器。 1、Serial(-XX:+UseSerialGC) 從名字我們可以看出,這是一個串行收集器。 Serial收集器是Java虛擬機中最基本、歷史最悠久的收集器。在JDK1.3之前是Java虛擬機新生代收集器的唯一選擇。目前也是ClientVM下ServerVM 4核4GB以下機器默認垃圾回收器。Serial收集器并不是只能使用一個CPU進行收集,而是當JVM需要進行垃圾回收的時候,需暫停所有的用戶線程,直到回收結束。
使用算法:復制算法
JVM中文名稱為Java虛擬機,因此它像一臺虛擬的電腦在工作,而其中的每一個線程都被認為是JVM的一個處理器,因此圖中的CPU0、CPU1實際上為用戶的線程,而不是真正的機器CPU,不要誤解哦。
Serial收集器雖然是最老的,但是它對于限定單個CPU的環境來說,由于沒有線程交互的開銷,專心做垃圾收集,所以它在這種情況下是相對于其他收集器中最高效的。
2、SerialOld(-XX:+UseSerialGC) SerialOld是Serial收集器的老年代收集器版本,它同樣是一個單線程收集器,這個收集器目前主要用于Client模式下使用。如果在Server模式下,它主要還有兩大用途:一個是在JDK1.5及之前的版本中與Parallel Scavenge收集器搭配使用,另外一個就是作為CMS收集器的后備預案,如果CMS出現Concurrent Mode Failure,則SerialOld將作為后備收集器。 使用算法:標記 - 整理算法 運行示意圖與上圖一致。
3、ParNew(-XX:+UseParNewGC) ParNew其實就是Serial收集器的多線程版本。除了Serial收集器外,只有它能與CMS收集器配合工作。
ParNew是許多運行在Server模式下的JVM首選的新生代收集器。但是在單CPU的情況下,它的效率遠遠低于Serial收集器,所以一定要注意使用場景。
4、ParallelScavenge(-XX:+UseParallelGC) ParallelScavenge又被稱為吞吐量優先收集器,和ParNew 收集器類似,是一個新生代收集器。 使用算法:復制算法 ParallelScavenge收集器的目標是達到一個可控件的吞吐量,所謂吞吐量就是CPU用于運行用戶代碼的時間與CPU總消耗時間的比值,即吞吐量 = 運行用戶代碼時間 / (運行用戶代碼時間 + 垃圾收集時間)。如果虛擬機總共運行了100分鐘,其中垃圾收集花了1分鐘,那么吞吐量就是99% 。
5、ParallelOld(-XX:+UseParallelOldGC) ParallelOld是并行收集器,和SerialOld一樣,ParallelOld是一個老年代收集器,是老年代吞吐量優先的一個收集器。這個收集器在JDK1.6之后才開始提供的,在此之前,ParallelScavenge只能選擇SerialOld來作為其老年代的收集器,這嚴重拖累了ParallelScavenge整體的速度。而ParallelOld的出現后,“吞吐量優先”收集器才名副其實! 使用算法:標記 - 整理算法
在注重吞吐量與CPU數量大于1的情況下,都可以優先考慮ParallelScavenge + ParalleloOld收集器。
6、CMS (-XX:+UseConcMarkSweepGC) CMS是一個老年代收集器,全稱 Concurrent Low Pause Collector,是JDK1.4后期開始引用的新GC收集器,在JDK1.5、1.6中得到了進一步的改進。它是對于響應時間的重要性需求大于吞吐量要求的收集器。對于要求服務器響應速度高的情況下,使用CMS非常合適。 CMS的一大特點,就是用兩次短暫的暫停來代替串行或并行標記整理算法時候的長暫停。 使用算法:標記 - 清理 CMS的執行過程如下: · 初始標記(STW initial mark) 在這個階段,需要虛擬機停頓正在執行的應用線程,官方的叫法STW(Stop Tow World)。這個過程從根對象掃描直接關聯的對象,并作標記。這個過程會很快的完成。 · 并發標記(Concurrent marking) 這個階段緊隨初始標記階段,在“初始標記”的基礎上繼續向下追溯標記。注意這里是并發標記,表示用戶線程可以和GC線程一起并發執行,這個階段不會暫停用戶的線程哦。 · 并發預清理(Concurrent precleaning) 這個階段任然是并發的,JVM查找正在執行“并發標記”階段時候進入老年代的對象(可能這時會有對象從新生代晉升到老年代,或被分配到老年代)。通過重新掃描,減少在一個階段“重新標記”的工作,因為下一階段會STW。 · 重新標記(STW remark) 這個階段會再次暫停正在執行的應用線程,重新重根對象開始查找并標記并發階段遺漏的對象(在并發標記階段結束后對象狀態的更新導致),并處理對象關聯。這一次耗時會比“初始標記”更長,并且這個階段可以并行標記。 · 并發清理(Concurrent sweeping) 這個階段是并發的,應用線程和GC清除線程可以一起并發執行。 · 并發重置(Concurrent reset) 這個階段任然是并發的,重置CMS收集器的數據結構,等待下一次垃圾回收。
CMS的缺點:
1、內存碎片。由于使用了 標記-清理 算法,導致內存空間中會產生內存碎片。不過CMS收集器做了一些小的優化,就是把未分配的空間匯總成一個列表,當有JVM需要分配內存空間的時候,會搜索這個列表找到符合條件的空間來存儲這個對象。但是內存碎片的問題依然存在,如果一個對象需要3塊連續的空間來存儲,因為內存碎片的原因,尋找不到這樣的空間,就會導致Full GC。
2、需要更多的CPU資源。由于使用了并發處理,很多情況下都是GC線程和應用線程并發執行的,這樣就需要占用更多的CPU資源,也是犧牲了一定吞吐量的原因。
3、需要更大的堆空間。因為CMS標記階段應用程序的線程還是執行的,那么就會有堆空間繼續分配的問題,為了保障CMS在回收堆空間之前還有空間分配給新加入的對象,必須預留一部分空間。CMS默認在老年代空間使用68%時候啟動垃圾回收。可以通過-XX:CMSinitiatingOccupancyFraction=n來設置這個閥值。
7、GarbageFirst(G1) 這是一個新的垃圾回收器,既可以回收新生代也可以回收老年代,SunHotSpot1.6u14以上EarlyAccess版本加入了這個回收器,Sun公司預期SunHotSpot1.7發布正式版本。通過重新劃分內存區域,整合優化CMS,同時注重吞吐量和響應時間。杯具的是Oracle收購這個收集器之后將其用于商用收費版收集器。因此目前暫時沒有發現哪個公司使用它,這個放在之后再去研究吧。
整理一下新生代和老年代的收集器。 新生代收集器: Serial (-XX:+UseSerialGC) ParNew(-XX:+UseParNewGC) ParallelScavenge(-XX:+UseParallelGC) G1 收集器
老年代收集器: SerialOld(-XX:+UseSerialOldGC) ParallelOld(-XX:+UseParallelOldGC) CMS(-XX:+UseConcMarkSweepGC) G1 收集器
8、調優jvm參數介紹 堆設置 -Xmx3550m:設置JVM最大堆內存 為3550M。 -Xms3550m:設置JVM初始堆內存 為3550M。此值可以設置與-Xmx相同,以避免每次垃圾回收完成后JVM重新分配內存。 -Xss128k:設置每個線程的棧 大小。JDK5.0以后每個線程棧大小為1M,之前每個線程棧大小為256K。應當根據應用的線程所需內存大小進行調整。在相同物理內存下,減小這個值能 生成更多的線程。但是操作系統對一個進程內的線程數還是有限制的,不能無限生成,經驗值在3000~5000左右。 -Xmn2g:設置堆內存年輕代 大小為2G。整個堆內存大小 = 年輕代大小 + 年老代大小 + 持久代大小 。持久代一般固定大小為64m,所以增大年輕代后,將會減小年老代大小。此值對系統性能影響較大,Sun官方推薦配置為整個堆的3/8。 -XX:PermSize=256M:設置堆內存持久代 初始值為256M。(貌似是Eclipse等IDE的初始化參數) -XX:MaxNewSize=size:新生成的對象能占用內存的最大值。 -XX:MaxPermSize=512M:設置持久代最大值為512M。 -XX:NewRatio=4:設置堆內存年輕代(包括Eden和兩個Survivor區)與堆內存年老代的比值(除去持久代) 。設置為4,則年輕代所占與年老代所占的比值為1:4。 -XX:SurvivorRatio=4:設置堆內存年輕代中Eden區與Survivor區大小的比值 。設置為4,則兩個Survivor區(JVM堆內存年輕代中默認有2個Survivor區)與一個Eden區的比值為2:4,一個Survivor區占 整個年輕代的1/6。 -XX:MaxTenuringThreshold=7:表示一個對象如果在救助空間(Survivor區)移動7次還沒有被回收就放入年老代。如果設置為0的話,則年輕代對象不經過Survivor區,直接進入年老代,對于年老代比較多的應用,這樣做可以提高效率。如果將此值設置為一個較大值,則年輕代對象會在Survivor區進行多次復制,這樣可以增加對象在年輕代存活時間,增加對象在年輕代即被回收的概率。
回收器選擇JVM給了三種選擇:串行收集器、并行收集器、并發收集器,但是串行收集器只適用于小數據量的情況,所以這里的選擇主要針對并行收集器和并發收集器。
默認情況下,JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在啟動時加入相應參數。JDK5.0以后,JVM會根據當前系統配置進行智能判斷。
串行收集器 -XX:+UseSerialGC:設置串行收集器 并行收集器(吞吐量優先) -XX:+UseParallelGC:選擇垃圾收集器為并行收集器。此配置僅對年輕代有效。即上述配置下,年輕代使用并發收集,而年老代仍舊使用串行收集。 -
XX:ParallelGCThreads=20:配置并行收集器的線程數,即:同時多少個線程一起進行垃圾回收。此值最好配置與處理器數目相等。 -XX:+UseParallelOldGC:配置年老代垃圾收集方式為并行收集。JDK6.0支持對年老代并行收集。 -
XX:MaxGCPauseMillis=100:設置每次年輕代垃圾回收的最長時間(單位毫秒),如果無法滿足此時間,JVM會自動調整年輕代大小,以滿足此值。 -XX:+UseAdaptiveSizePolicy:設置此選項后,并行收集器會自動選擇年輕代區大小和相應的Survivor區比例,以達到目標系統規定的最低響應時間或者收集頻率等。 此參數建議使用并行收集器時,一直打開。 并發收集器(響應時間優先) -XX:+UseParNewGC:設置年輕代為并發收集。可與CMS收集同時使用。JDK5.0以上,JVM會根據系統配置自行設置,所以無需再設置此值。 CMS, 全稱Concurrent Low Pause Collector,是jdk1.4后期版本開始引入的新gc算法,在jdk5和jdk6中得到了進一步改進,它的主要適合場景是對響應時間的重要性需求 大于對吞吐量的要求,能夠承受垃圾回收線程和應用線程共享處理器資源,并且應用中存在比較多的長生命周期的對象的應用。CMS是用于對tenured generation的回收,也就是年老代的回收,目標是盡量減少應用的暫停時間,減少FullGC發生的幾率,利用和應用程序線程并發的垃圾回收線程來 標記清除年老代。 -XX:+UseConcMarkSweepGC:設置年老代為并發收集。測試中配置這個以后,-XX:NewRatio=4的配置失效了。所以,此時年輕代大小最好用-Xmn設置。 -XX:CMSFullGCsBeforeCompaction=:由于并發收集器不對內存空間進行壓縮、整理,所以運行一段時間以后會產生“碎片”,使得運行效率降低。此參數設置運行次FullGC以后對內存空間進行壓縮、整理。 -XX:+UseCMSCompactAtFullCollection:打開對年老代的壓縮。可能會影響性能,但是可以消除內存碎片。 -XX:+CMSIncrementalMode:設置為增量收集模式。一般適用于單CPU情況。 -XX:CMSInitiatingOccupancyFraction=70:表示年老代空間到70%時就開始執行CMS,確保年老代有足夠的空間接納來自年輕代的對象。 注:如果使用 throughput collector 和 concurrent low pause collector 這兩種垃圾收集器,需要適當的挺高內存大小,為多線程做準備。 其它 -XX:+ScavengeBeforeFullGC:新生代GC優先于Full GC執行。 -XX:-DisableExplicitGC:禁止調用System.gc(),但JVM的gc仍然有效。 -XX:+MaxFDLimit:最大化文件描述符的數量限制。 -XX:+UseThreadPriorities:啟用本地線程優先級API,即使 java.lang.Thread.setPriority() 生效,反之無效。 -XX:SoftRefLRUPolicyMSPerMB=0:“軟引用”的對象在最后一次被訪問后能存活0毫秒(默認為1秒)。 -XX:TargetSurvivorRatio=90:允許90%的Survivor空間被占用(默認為50%)。提高對于Survivor的使用率——超過就會嘗試垃圾回收。
輔助信息 -XX:-CITime:打印消耗在JIT編譯的時間 -XX:ErrorFile=./hs_err_pid.log:保存錯誤日志或者數據到指定文件中 -XX:-ExtendedDTraceProbes:開啟solaris特有的dtrace探針 -XX:HeapDumpPath=./java_pid.hprof:指定導出堆信息時的路徑或文件名 -XX:-HeapDumpOnOutOfMemoryError:當首次遭遇內存溢出時導出此時堆中相關信息 -XX:OnError=";":出現致命ERROR之后運行自定義命令 -XX:OnOutOfMemoryError=";":當首次遭遇內存溢出時執行自定義命令 -XX:-PrintClassHistogram:遇到Ctrl-Break后打印類實例的柱狀信息,與jmap -histo功能相同 -XX:-PrintConcurrentLocks:遇到Ctrl-Break后打印并發鎖的相關信息,與jstack -l功能相同 -XX:-PrintCommandLineFlags:打印在命令行中出現過的標記 -XX:-PrintCompilation:當一個方法被編譯時打印相關信息 -XX:-PrintGC:每次GC時打印相關信息 -XX:-PrintGC Details:每次GC時打印詳細信息 -XX:-PrintGCTimeStamps:打印每次GC的時間戳 -XX:-TraceClassLoading:跟蹤類的加載信息 -XX:-TraceClassLoadingPreorder:跟蹤被引用到的所有類的加載信息 -XX:-TraceClassResolution:跟蹤常量池 -XX:-TraceClassUnloading:跟蹤類的卸載信息 -XX:-TraceLoaderConstraints:跟蹤類加載器約束的相關信息
JVM服務調優實戰 服務器:8 cup, 8G mem e.g. java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
調優方案: -Xmx5g:設置JVM最大可用內存為5G。 -Xms5g:設置JVM初始內存為5G。此值可以設置與-Xmx相同,以避免每次垃圾回收完成后JVM重新分配內存。 -Xmn2g:設置年輕代大小為2G。整個堆內存大小 = 年輕代大小 + 年老代大小 + 持久代大小 。持久代一般固定大小為64m,所以增大年輕代后,將會減小年老代大小。此值對系統性能影響較大,Sun官方推薦配置為整個堆的3/8。 -XX:+UseParNewGC:設置年輕代為并行收集。可與CMS收集同時使用。JDK5.0以上,JVM會根據系統配置自行設置,所以無需再設置此值。 -XX:ParallelGCThreads=8:配置并行收集器的線程數,即:同時多少個線程一起進行垃圾回收。此值最好配置與處理器數目相等。 -XX:SurvivorRatio=6:設置年輕代中Eden區與Survivor區的大小比值。根據經驗設置為6,則兩個Survivor區與一個Eden區的比值為2:6,一個Survivor區占整個年輕代的1/8。 -XX:MaxTenuringThreshold=30:設置垃圾最大年齡(次數)。如果設置為0的話,則年輕代對象不經過Survivor區直接進入年老代。對于年老代比較多的應用,可以提高效率。如果將此值 設置為一個較大值,則年輕代對象會在Survivor區進行多次復制,這樣可以增加對象再年輕代的存活時間,增加在年輕代即被回收的概率。設置為30表示 一個對象如果在Survivor空間移動30次還沒有被回收就放入年老代。 -XX:+UseConcMarkSweepGC:設置年老代為并發收集。測試配置這個參數以后,參數-XX:NewRatio=4就失效了,所以,此時年輕代大小最好用-Xmn設置,因此這個參數不建議使用。
參考資料 - JVM堆內存的分代 虛擬機的堆內存共劃分為三個代:年輕代(Young Generation)、年老代(Old Generation)和持久代(Permanent Generation)。其中持久代主要存放的是Java類的類信息,與垃圾收集器要收集的Java對象關系不大。所以,年輕代和年老代的劃分才是對垃圾 收集影響比較大的。
年輕代
所有新生成的對象首先都是放在年輕代的。年輕代的目標就是盡可能快速的收集掉那些生命周期短的對象。年輕代分三個區。一個Eden區,兩個 Survivor區(一般而言)。 大部分對象在Eden區中生成。當Eden區滿時,還存活的對象將被復制到Survivor區(兩個中的一個),當一個Survivor區滿 時,此區的存活對象將被復制到另外一個Survivor區,當另一個Survivor區也滿了的時候,從前一個Survivor區復制過來的并且此時還存 活的對象,將被復制“年老區(Tenured)”。 需要注意,兩個Survivor區是對稱的,沒先后關系,所以同一個Survivor區中可能同時存在從Eden區復制過來對象,和從另一個 Survivor區復制過來的對象;而復制到年老區的只有從前一個Survivor區(相對的)過來的對象。而且,Survivor區總有一個是空的。特 殊的情況下,根據程序需要,Survivor區是可以配置為多個的(多于兩個),這樣可以增加對象在年輕代中的存在時間,減少被放到年老代的可能。
年老代
在年輕代中經歷了N(可配置)次垃圾回收后仍然存活的對象,就會被放到年老代中。因此,可以認為年老代中存放的都是一些生命周期較長的對象。
持久代 用于存放靜態數據,如 Java Class, Method 等。持久代對垃圾回收沒有顯著影響,但是有些應用可能動態生成或者調用一些Class,例如 Hibernate 等,在這種時候需要設置一個比較大的持久代空間來存放這些運行過程中動態增加的類型。持久代大小通過 -XX:MaxPermSize= 進行設置。
目前了解的GC收集器就是這么多了,如果有哪位有緣的朋友看到了這篇文章,恰好有更好的GC收集器推薦,歡迎留言交流。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。