GC三件事
- 哪些內存需要回收
- 何時回收
- 怎么回收
主要考慮的是堆
和方法區
如何判斷對象已死
1、引用計數:實現簡單,教科書答案,java虛擬機沒有采用
2、根搜索算法:
基本思路就是通過一系列名為"GC Roots"的對象作為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain),當一個對象到GC Roots沒有任何引用鏈相連時,則證明此對象是不可用的。
3、java里面可以作為GCRoots的對象:
a.虛擬機棧(棧楨中的本地變量表)中的引用的對象
b.方法區中的類靜態屬性引用的對象
c.方法區中的常量引用的對象
d.本地方法棧中JNI的引用的對象
4、如何判斷一個對象已死
- 該類所有的實例都已經被回收,也就是Java堆中不存在該類的任何實例。
- 加載該類的ClassLoader已經被回收。
- 該類對應的java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問該
類的方法
5、垃圾收集算法
算法名稱 | 英文名 | 描述 | 優點 | 缺點 |
---|---|---|---|---|
標記-清除 | Mark-Sweep | 算法分為“標記”和“清除”兩個階段:首先標記出所有需要回收的對象,在標記完成后統一回收所有被標記的對象 | 1、效率不高;2、內存碎片,效率不高 | |
復制算法 | Copying | 將內存分為一塊較大的Eden空間和兩塊較小的Survivor空間,8:1,內存利用率90% | 解決了效率問題【適合新生代】 | 利用率低了 |
標記-整理 | Mark-Compact | 標記過程仍然與“標記-清除”算法一樣,但后續步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,然后直接清理掉端邊界以外的內存 | 減少復制,減少空間浪費 | 無 |
分代收集算法 | Generational Collection | 根據對象存活周期的不同將內存劃分為幾塊。一般為新生代和老生代,新生代采用復制算法,老生代采用標記-清除 或是 標記-整理
|
無 |
6、垃圾收集器
如果說收集算法是內存回收的方法論,那么垃圾收集器就是內存回收的具體實現
HotSpot虛擬機的垃圾收集器
名字 | 新\老 生代 | 原理 | 優點 | 缺點 | 備注 |
---|---|---|---|---|---|
Serial收集器 | 新 |
復制算法 ;收集器是一個單線程的收集器;必須暫停其他所有的工作線程,直到它收集結束 |
簡單而高效,少了線程交互開銷 | Stop The World | Client模式下的虛擬機新生代默認選擇 |
ParNew收集器 | 新 | ParNew收集器其實就是Serial收集器的多線程 版本 |
多線程 | 單CPU還不如Serial收集器 | 是許多運行在Server模式下的虛擬機中首選的新生代收集器,其中有一個與性能無關但很重要的原因是,除了Serial收集器外,目前只有它能與CMS收集器配合工作 |
Parallel Scavenge收集器 | 新 | Parallel Scavenge收集器的目標則是達到一個可控制的吞吐量(Throughput)。所謂吞吐量就是CPU用于運行用戶代碼的時間與CPU總消耗時間的比值,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間) | 復制算法;并行 | 無 | |
Serial Old收集器 | 老 | 使用“標記-整理”算法 | 無 | ||
Parallel Old收集器 | 老 | Parallel Scavenge收集器的老年代版本,使用多線程和“標記-整理”算法 | 無 | ||
CMS收集器(Concurrent Mark Sweep) | 老 | CMS收集器是基于“標記—清除”算法實現的 | 無 |
并行(Parallel):指多條垃圾收集線程并行工作,但此時用戶線程仍然處于等待狀
態。
并發(Concurrent):指用戶線程與垃圾收集線程同時執行(但不一定是并行的,可能
會交替執行),用戶程序在繼續運行,而垃圾收集程序運行于另一個CPU上。
7、理解GC日志
2017-03-24T11:18:49.803+0800: 311976.229: [GC (Allocation Failure) 2017-03-24T11:18:49.803+0800: 311976.229: [ParNew: 1428004K->26220K(1485504K), 0.0369712 secs] 3356779K->1955081K(5155520K), 0.0373455 secs] [Times: user=0.13 sys=0.00, real=0.04 secs]
2017-03-24T11:18:49.803+0800:
【1】 311976.229:
【2】 [GC
【3】 (Allocation Failure
【4】) 2017-03-24T11:18:49.803+0800: 311976.229: [ParNew:
【5】 1428004K->26220K
【6】 (1485504K
【7】), 0.0369712 secs
【8】] 3356779K->1955081K
【9】(5155520K
【10】), 0.0373455 secs
【11】] [Times: user=0.13 sys=0.00, real=0.04 secs
【12】]
詳解:
-
2017-03-24T11:18:49.803+0800
【1】GC事件(GC event)開始的時間點 -
311976.229:
【2】GC時間的開始時間,相對于JVM的啟動時間,單位是秒(Measured in seconds) -
GC
【3】用來區分(distinguish)是 Minor GC 還是 Full GC 的標志(Flag). 這里的 GC 表明本次發生的是 Minor GC. -
Allocation Failure
【4】引起垃圾回收的原因. 本次GC是因為年輕代中沒有任何合適的區域能夠存放需要分配的數據結構而觸發的. -
ParNew:
【5】使用的垃圾收集器的名字 -
1428004K->26220K
【6】在本次垃圾收集之前和之后的年輕代內存使用情況(Usage) -
1485504K
【7】年輕代的總的大小(Total size). -
0.0369712 secs
【8】該內存區域GC所占用的時間,單位是秒 -
3356779K->1955081K
【9】在本次垃圾收集之前和之后整個堆內存的使用情況(Total used heap) -
5155520K
【10】總的可用的堆內存(Total available heap). -
0.0373455 secs
【11】GC事件的持續時間(Duration),單位是秒 -
Times: user=0.13 sys=0.00, real=0.04 secs
【12】這里面的user、
sys和real與Linux的time命令所輸出的時間含義一致,分別代表用戶態
消耗的CPU時間、內核 態
消耗的CPU事件和操作從開始到結束所經過的墻鐘時間
(Wall Clock Time)。CPU時間與
墻鐘時間的區別是,墻鐘時間包括各種非運算的等待耗時.
8、內存分配與回收策略
- 對象優先在Eden分配
package study3;
/**
*
* -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
* @date 2017/03/24
*/
public class TestAllocation {
public static void main(String[] args) {
final int _1MB = 1024 * 1024;
byte[] allocation1, allocation2, allocation3, allocation4;
allocation1 = new byte[2 * _1MB];
allocation2 = new byte[2 * _1MB];
allocation3 = new byte[2 * _1MB];
// 出現一次Minor GC
allocation4 = new byte[4 * _1MB];
}
}
[GC [ParNew: 8118K->473K(9216K), 0.0075132 secs] 8118K->473K(19456K), 0.0086882 secs] [Times: user=0.00 sys=0.01, real=0.01 secs]
Heap
par new generation total 9216K, used 4981K [7f9a00000, 7fa400000, 7fa400000)
eden space 8192K, 55% used [7f9a00000, 7f9e66db0, 7fa200000)
from space 1024K, 46% used [7fa300000, 7fa3767f0, 7fa400000)
to space 1024K, 0% used [7fa200000, 7fa200000, 7fa300000)
concurrent mark-sweep generation total 10240K, used 0K [7fa400000, 7fae00000, 7fae00000)
concurrent-mark-sweep perm gen total 21248K, used 4898K [7fae00000, 7fc2c0000, 800000000)
- 大對象直接進入老年代
- 長期存活的對象將進入老年代