垃圾回收和內存分配相關,先了解運行時數據區域的劃分及各個區域的做用。java
垃圾回收主要須要考慮的3個問題:算法
一、何時回收;數組
二、哪些對象須要回收;spa
三、如何回收。線程
程序計數器是一塊較小的內存空間,它是當前線程執行字節碼的行號指示器,字節碼解釋工做器就是經過改變這個計數器的值來選取下一條須要執行的指令。它是線程私有的內存,也是惟一一個沒有OOM異常的區域。3d
也就是一般所說的棧區,它描述的是Java方法執行的內存模型,每一個方法被執行的時候都建立一個棧幀(Stack Frame),用於存儲局部變量表、操做數棧、動態連接、方法出口等。每一個方法被調用到完成,至關於一個棧幀在虛擬機棧中從入棧到出棧的過程。此區域也是線程私有的內存,可能拋出兩種異常:若是線程請求的棧深度大於虛擬機容許的深度將拋出StackOverflowError;若是虛擬機棧能夠動態的擴展,擴展到沒法動態的申請到足夠的內存時會拋出OOM異常。對象
本地方法棧與虛擬機棧發揮的做用很是類似,區別就是虛擬機棧爲虛擬機執行Java方法,本地方法棧則是爲虛擬機使用到的Native方法服務。blog
全部對象實例和數組都在堆區上分配,堆區是GC主要管理的區域。堆區還能夠細分爲新生代、老年代,新生代還分爲一個Eden區和兩個Survivor區。此塊內存爲全部線程共享區域,當堆中沒有足夠內存完成實例分配時會拋出OOM異常。內存
方法區也是全部線程共享區,用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯後的代碼等數據。GC在這個區域不多出現,這個區域內存回收的目標主要是對常量池的回收和類型的卸載,回收的內存比較少,因此也有稱這個區域爲永久代(Permanent Generation)的。當方法區沒法知足內存分配時拋出OOM異常。運行時常量池是方法區的一部分,用於存放編譯期生成的各類字面量和符號引用。資源
給對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加1 ;當引用失效時,計數器值就減1 ;任什麼時候刻計數器都爲0 的對象就是不可能再被使用的。引用計數算法(Reference Counting)的實現簡單,斷定效率也很高,在大部分狀況下它都是一個不錯的算法,可是Java 語言中沒有選用引用計數算法來管理內存,其中最主要的緣由是它很難解決對象之間的相互循環引用的問題。
這個算法的基本思路就是經過一系列的名爲「GC Roots」的對象做爲起始點,從這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈(Reference Chain),當一個對象到GC Roots 沒有任何引用鏈相連(用圖論的話來講就是從GC Roots 到這個對象不可達)時,則證實此對象是不可用的。在Java 語言裏,可做爲GC Roots 的對象包括下面幾種:
GC常常發生的區域是堆區,堆區還能夠細分爲新生代、老年代,新生代還分爲一個Eden區和兩個Survivor區。
A.對象優先在Eden中分配,當Eden中沒有足夠空間時,虛擬機將發生一次Minor GC,Minor GC很是頻繁,並且速度也很快;
B.Full GC,發生在老年代的GC,當老年代沒有足夠的空間時即發生Full GC,發生Full GC通常都會有一次Minor GC。
C.發生Minor GC時,虛擬機會檢測以前每次晉升到老年代的平均大小是否大於老年代的剩餘空間大小,若是大於,則進行一次Full GC,若是小於,則查看是否容許擔保失敗,若是容許,那隻會進行一次Minor GC,若是不容許,則改成進行一次Full GC。
先看看經常使用的垃圾回收算法。
缺點:標記、清除的效率都很低;標記清除後致使不連續空間。
優勢:存活率低時高效,回收後空間連續。
缺點:內存分紅大小相同的兩塊,資源浪費。存活率高的狀況下複製的對象多,效率低。
HotSpot內存分配:Eden :Survivor:Survivor = 8:1:1。每次只有10%的內存是可能被浪費的。
根據對象的存活週期將內存分爲新生代和老年代。
新生代中的對象都是朝生夕死的對象,老年代中的對象相對比較穩定。
新生代和老年代採用不一樣的收集算法。新生代的特色對象存活率很低(複製算法);老年代的特色對象存活率高,沒有額外的空間進行分配擔保(標記整理算法)。
A.大對象。
B.每次Eden進行MinorGC後對象年齡加1進入survivor,對象年齡達到15時進入老年代。
C.若是Survivor空間中相同年齡全部對象大小的總和大於survivor空間的一半,年齡大於等於該年齡的對象就直接進入老年代。
D.若是survivor空間不能容納Eden中存活的對象。因爲擔保機制會進入老年代。若是survivor中的對象存活不少,擔保失敗,那麼會進行一次Full GC。
總結:本文參考《深刻理解java虛擬機》進行總結,若有不妥之處請讀者批評教導。