GC日誌 Heap PSYoungGen total 305664K, used 26214K [0x00000000eab00000, 0x0000000100000000, 0x0000000100000000) eden space 262144K, 10% used [0x00000000e ...
GC日誌
-Xmx1024m -Xms1024m -XX:+PrintGCDetails
Heap
PSYoungGen total 305664K, used 26214K [0x00000000eab00000, 0x0000000100000000, 0x0000000100000000)
eden space 262144K, 10% used [0x00000000eab00000,0x00000000ec499be8,0x00000000fab00000)
from space 43520K, 0% used [0x00000000fd580000,0x00000000fd580000,0x0000000100000000)
to space 43520K, 0% used [0x00000000fab00000,0x00000000fab00000,0x00000000fd580000)
ParOldGen total 699392K, used 0K [0x00000000c0000000, 0x00000000eab00000, 0x00000000eab00000)
object space 699392K, 0% used [0x00000000c0000000,0x00000000c0000000,0x00000000eab00000)
Metaspace used 3224K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 351K, capacity 388K, committed 512K, reserved 1048576K
下麵再編寫一個代碼,觀察GC的觸發操作
public class Demo {
public static void main(String[] args){
Random random = new Random();
String val = "test";
while (true){
val+=val+random.nextInt(999999999)+random.nextInt(999999999);
}
}
}
[GC (Allocation Failure) [PSYoungGen: 2031K->488K(2560K)] 2031K->676K(9728K), 0.0013870 secs] [Times: user=0.06 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 2441K->504K(2560K)] 2629K->1254K(9728K), 0.0010120 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 1950K->488K(2560K)] 2700K->1951K(9728K), 0.0011297 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 1940K->488K(2560K)] 4796K->4049K(9728K), 0.0012419 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 1257K->488K(2560K)] 6212K->5443K(9728K), 0.0009412 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 488K->496K(1536K)] 5443K->5491K(8704K), 0.0005513 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 496K->0K(1536K)] [ParOldGen: 4995K->2727K(7168K)] 5491K->2727K(8704K), [Metaspace: 3281K->3281K(1056768K)], 0.0066911 secs] [Times: user=0.09 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [PSYoungGen: 30K->32K(2048K)] 6938K->6940K(9216K), 0.0004666 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Ergonomics) [PSYoungGen: 32K->0K(2048K)] [ParOldGen: 6908K->2030K(7168K)] 6940K->2030K(9216K), [Metaspace: 3281K->3281K(1056768K)], 0.0082892 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [PSYoungGen: 19K->0K(2048K)] 6231K->6211K(9216K), 0.0003457 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Ergonomics) [PSYoungGen: 0K->0K(2048K)] [ParOldGen: 6211K->4817K(7168K)] 6211K->4817K(9216K), [Metaspace: 3281K->3281K(1056768K)], 0.0027242 secs] [Times: user=0.08 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] 4817K->4817K(9216K), 0.0003852 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] [ParOldGen: 4817K->4798K(7168K)] 4817K->4798K(9216K), [Metaspace: 3281K->3281K(1056768K)], 0.0095410 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
GC日誌分析
JVM垃圾回收
垃圾對象判定標準
jvm的GC工作主要針對的對象是堆記憶體,在做GC工作之前,首先要判定堆記憶體中的對象實例是否為垃圾,通常使用以下兩種演算法來定義
1.引用計數演算法
java在運行時,當有一個地方引用該對象實例,會將這個對象實例加1,引用失效時就減1,jvm在掃描記憶體時,發現引用計數值為0的則是垃圾對象,計數值大於0的則為活躍對象。
目前垃圾回收演算法,沒有採用引用計數演算法,原因是在對象互相引用的情況下,無法判定兩者是否為垃圾對象。
2. 根搜索演算法
根搜索演算法是以“GC ROOTS”為起始點往下搜索,所有經過的對象合併起來稱為引用鏈,在這引用鏈里,沒有的對象稱為垃圾對象,在引用鏈里的是活躍對象。那什麼樣的對象才能稱為“GC ROOTS”呢?以下四種可以
- 虛擬機棧(棧幀中的本地變數表)中引用的對象。
- 方法區中的類靜態屬性引用的對象。
- 方法區中的常量引用的對象。
- 本地方法棧中 JNI(Native 方法)的引用對象。
垃圾回收演算法
1. 標記-清除(Mark-Sweep)
jvm會掃描所有的對象實例,通過根搜索演算法,將活躍對象進行標記,jvm再一次掃描所有對象,將未標記的對象進行清除,只有清除動作,不作任何的處理,這樣導致的結果會存在很多的記憶體碎片。
2. 複製(copying)
jvm掃描所有對象,通過根搜索演算法標記被引用的對象,之後會申請新的記憶體空間,將標記的對象複製到新的記憶體空間里,存活的對象複製完,會清空原來的記憶體空間,將新的記憶體最為jvm的對象存儲空間。這樣雖然解決了記憶體記憶體碎片問題,但是如果對象很多,重新申請新的記憶體空間會很大,在記憶體不足的場景下,會對jvm運行造成很大的影響
3. 標記-整理(Mark-compact)
標記整理實際上是在標記清除演算法上的優化,執行完標記清除全過程之後,再一次對記憶體進行整理,將所有存活對象統一向一端移動,這樣解決了記憶體碎片問題。
4. 分代回收
目前jvm常用回收演算法就是分代回收,年輕代以複製演算法為主,老年代以標記整理演算法為主。原因是年輕代對象比較多,每次垃圾回收都有很多的垃圾對象回收,而且要儘可能快的減少生命周期短的對象,存活的對象較少,這時候複製演算法比較適合,只要將有標記的對象複製到另一個記憶體區域,其餘全部清除,並且複製的數量較少,效率較高;而老年代是年輕代篩選出來的對象,被標記比較多,需要刪除的對象比較少,顯然採用標記整理效率較高。