摘要:引言垃圾收集技術并不是語言首創的,年誕生于的是第一門真正使用內存動態分配和垃圾收集技術的語言。垃圾收集器所關注的就是這部分內存。收集器是收集器的多線程版,它是第一款并發收集器。經常出現大對象會導致多次出發垃圾收集。
引言
垃圾收集技術并不是Java語言首創的,1960年誕生于MIT的Lisp是第一門真正使用內存動態分配和垃圾收集技術的語言。垃圾收集技術需要考慮的三個問題是:
哪些內存需要回收?
什么時候回收?
如何回收?
http://segmentfault.com/a/119... 中講到java內存運行時區域的分布,其中程序計數器,虛擬機棧,本地方法區都是隨著線程而生,隨線程而滅,所以這幾個區域就不需要過多考慮回收問題。但是堆和方法區就不一樣了,只有在程序運行期間我們才知道會創建哪些對象,這部分內存的分配和回收都是動態的。垃圾收集器所關注的就是這部分內存。
一 對象死亡判據垃圾收集器在對一個對象回收之前,首先要判斷對象在程序中是否還有使用的可能性,充要條件就是沒有被程序可訪問的引用再指向這個對象實例。最簡單的辦法就是給對象實例添加中添加一個引用計數器,每當有一個引用指向它時,計數器就加一,當引用失效時,計數器就減一,如果計數器值為0則說明沒有引用指向它,可以進行回收。但是這個方法中計數器為0并不是一個必要條件,例如,生成兩個對象實例,每個對象實例的屬性都指向對方,那么這個兩個對象實例分別最少有一個引用。
java采用的是可達性分析算法,即找一部分對象作為"GC Roots"節點,從這些節點開始向下搜索,當某個對象到"GC Roots"節點沒有可達路徑時,說明此對象是不可用的。在java中作為"GC Roots"的節點包括:
虛擬機棧中引用的對象,
方法區靜態屬性引用的對象,
方法區常量引用的對象,
本地方法區中本地調用所引用的對象。
引用擴充如果reference類型的數據中存儲的數值是另一塊內存的起始地址,那么這塊內存就代表著一個引用。一個對象在這種狀態下,只能有被引用和沒有被引用兩種狀態。java對引用概念進行了擴充,將引用分為強引用(new),軟引用(softReference),弱引用(WeakReference),虛引用(PhantomReference)。如果強引用存在,則垃圾收集器不會回收該對象。如果系統即將發生內存溢出異常,那么垃圾回收集器則會回收軟引用對象。弱引用對象只能存活到下一次垃圾收集之前。虛引用對象不會對其生存時間構成任何影響。
對象的自我救贖在垃圾收集器發現某一個對象到"GC Roots"路徑不可達時,先會判斷該對象是否覆蓋finalize()方法,或是否執行過finalize()方法。如果覆蓋了且沒有執行過該方法,則會將該對象放到低優先級的Finalizer線程隊列中去執行finalize()方法,如果在finalize()方法中該對象又被引用,則會有一次逃脫被回收的命運。
方法區的回收方法區中主要回收廢棄的常量和無用的類。對于常量,如果沒有引用指向常量,則該常量會被回收。對于類的回收則麻煩許多,首先要判斷該類是無用的類,無用的類要滿足三個條件:
所有類的實例被回收。
加載該類的ClassLoader已經被回收。
Class沒有被引用,不會通過反射訪問該類的方法。
二 垃圾回收算法 標記-清除算法(Mark-Sweep)該算法分為兩個階段:首先標記處要回收的對象,標記完成后統一回收所有被標記的對象。
存在的問題:
標記和清除效率都不高
標記清除后會產生大量內存碎片,分配大對象時可能觸發另一次垃圾收集。
復制算法(Copying)該算法將內存分為兩個等大小的區域,每次只使用一個區域。當一個區域快用完了,就將這個區域中存活的對象復制到另一個區域。
優點是避免了內存碎片的產生,缺點是浪費內存空間。
有公司研究表明,新生代的對象98%都是朝生暮死,所以虛擬機把新生代內存劃分為一個較大的Eden空間和兩個較小的Survivor空間。每次只是用Eden空間和一個Survior空間,當進行復制清理時,將Survivor空間和Eden空間中存活的對象復制到另一塊Survivor空間。當Survivor空間不夠用時,就會依賴老年代進行分配擔保。
標記-整理算法(Mark-Compact)針對老年代對象存活率高的情況,復制算法明顯不合適,于是采用標記整理算法,標記和標記清除算法相同,二后邊的整理則是讓所有存活的對象都向一端移動,然后清理掉邊界外的內存。
分代收集當前虛擬機都采用分代收集,分代的依據是對象的存活周期。一般新生代存活率低,采用復制算法。老年代存活率高采用標記整理或標記清除。
一般來講,新生代空間會小很多,具體比例一般要看應用場景。而默認的大小一般是老年代的1/4到1/3。
三 垃圾收集器
由于虛擬機采用了分代收集,所以針對不同代收集器也不同。上圖是HotSpot虛擬機的垃圾收集器,連線表示可以協同工作。
Serial收集器,復制算法,它是一個單線程的收集器,并且在進行收集時會暫停其他線程,它默認是client模式下的新生代收集器。
ParNew收集器是Serial收集器的多線程版,它是第一款并發收集器。
Parallel Scavenge收集器可以精確控制吞吐量(用戶代碼運行時間/(用戶代碼時間+垃圾收集時間))
SerialOld收集器是serial收集器的老年版,采用標記整理算法,同樣是單線程收集器。
ParallelOld是ParallelScavenge收集器的老年版,使用多線程和標記整理算法。
CMS收集器是以最短回收停頓時間為目標的收集器,采用標記清除算法,在重視響應速度的系統中得以應用。但是缺點是對CPU資源敏感,無法處理浮動垃圾,易產生內存碎片。
G1收集器是最新推出的收集器,可應用在JDK1.7u4及以上版本。它將內存分為多個Region,新生代和老年代分別包含多個Region。G1跟蹤各個Region,判斷垃圾價值大小,優先回收價值最大的Region。
安全點安全點的概念是指當進行GC時,應當讓工作線程停止,這時會更容易對對象是否存活進行判斷。而停止線程應當在安全的時刻,所以會有安全點的概念。
暫停線程有兩種方式,第一種是強制暫停,如果某些線程沒有到達安全點則再讓他運行到安全點,這叫做搶先式中斷。第二種是在編譯過程中在安全點加入一個條件判斷判斷0x160100內存頁是否可讀,如果不可讀則會暫停,這叫做主動式中斷。
四 內存分配與回收策略
對象的分配,就是在堆上分配,對象主要分配在新生代的Eden區域中,如果啟動了本地線程分配緩沖,則按線程優先在TLAB中分配。少數情況也有可能直接分配到老年代。
對象在Eden區域分配時,當Eden區域沒有足夠空間,虛擬機會發起一次新生代垃圾收集。
如果對象需要大量連續內存空間,例如String類型和數組。大對象對于虛擬機內存分配來說是一個壞消息,朝生暮死的大對象是要命的壞消息。經常出現大對象會導致多次出發垃圾收集。對于這類對象,可以設置參數將大對象直接存入老年代。
每一個對象都有一個年齡計數器,當對象在Eden區域出生,每經過一次GC,并且存入Survivor,計數器加一。當年齡增加到一定程度(默認15),則會被存入老年代。同時,如果Survivor空間中相同年齡對象占空間超過50%,則也會直接進入老年代。
總結垃圾收集算法:復制算法,標記-清除算法,標記-清理算法。
垃圾收集器特點:新生代用復制,老年代用標記清理,CMS用標記清除。
Eden空間大小和Survivor空間大小默認比率為8:1,即新生代10%的空間用來存放復制后的對象。
更多文章:http://blog.gavinzh.com
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/64403.html
摘要:目錄往期博客課堂篇初識常量池簡單理解字符串常量池靜態常量池大整型常量池為什么要了解垃圾收集和內存分配如何判斷對象已死引用計數算法可達性分析算法之后引用的擴充回收方法區垃圾收集算法分代收集理論標記清除標記復制標記整理對象分 ...
摘要:虛擬機所處的區域,則表示它是屬于新生代收集器還是老年代收集器。虛擬機總共運行了分鐘,其中垃圾收集花掉分鐘,那么吞吐量就是。收集器線程所占用的數量為。 本文主要從GC(垃圾回收)的角度試著對jvm中的內存分配策略與相應的垃圾收集器做一個介紹。 注:還是老規矩,本著能畫圖就不BB原則,盡量將各知識點通過思維導圖或者其他模型圖的方式進行說明。文字僅記錄額外的思考與心得,以及其他特殊情況 內存...
摘要:當兩個對象相互引用時,這兩個對象就不會被回收引用計數算法不被主流虛擬機采用,主要原因是它很難解決對象之間相互循環引用的問題。 垃圾收集器與內存分配策略 詳解 3.1 概述 本文參考的是周志明的 《深入理解Java虛擬機》第三章 ,為了整理思路,簡單記錄一下,方便后期查閱。 3.2 對象已死嗎 在垃圾收集器進行回收前,第一件事就是確定這些對象哪些還存活,哪些已經死去。 3.2.1 引用...
摘要:堆和方法區只有在程序運行時才能確定內存的使用情況,垃圾回收器所關注的主要就是這部分內存。虛擬機會根據當前系統的運行情況收集性能監控信息,動態調整比率參數以提供最合適的停頓時間或最大的吞吐量。 Tip:內容為對《深入理解Java虛擬機》(周志明 著)第三章內容的總結和筆記。這是第一次拜讀時讀到的一些重點,做個分享,也為后面再次閱讀和實踐做保障。 3.1 概述 程序計數器、虛擬機棧、本地...
摘要:第章內存區域與內存溢出異常運行時數據區域虛擬機在執行程序的過程中會把它所管理的內存劃分為若干個不同的數據區域。即對象指向它的類元數據的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例。 第2章 Java內存區域與內存溢出異常 2.2 運行時數據區域 Java虛擬機在執行Java程序的過程中會把它所管理的內存劃分為若干個不同的數據區域。根據《Java虛擬機規范(Java SE 7版)...
閱讀 3280·2023-04-26 02:42
閱讀 791·2021-10-09 09:41
閱讀 3191·2021-09-06 15:02
閱讀 700·2019-08-26 10:45
閱讀 480·2019-08-23 15:53
閱讀 733·2019-08-22 18:10
閱讀 550·2019-08-22 18:01
閱讀 3517·2019-08-22 17:34