public class Demo {
public static void main(String[] args) {
byte[] array1 = new byte[4 * 1024 * 1024];
array1 = null;
byte[] array2 = new byte[2 * 1024 * 1024];
byte[] array3 = new byte[2 * 1024 * 1024];
byte[] array4 = new byte[2 * 1024 * 1024];
byte[] array5 = new byte[128 * 1024];
byte[] array6 = new byte[2 * 1024 * 1024];
}
}
参数:
-XX:NewSize=10485760 -XX:MaxNewSize=10485760 -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -XX:PretenureSizeThreshold=3145728 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log
-XX:PretenureSizeThreshold=3145728
: 设置大对象阈值为 3MB,也就是超过 3MB,就直接进入老年代。
GC 日志如下:
OpenJDK 64-Bit Server VM (25.162-b12) for linux-amd64 JRE (1.8.0_162-8u162-b12-1-b12), built on Mar 15 2018 17:19:50 by "buildd" with gcc 7.3.0
Memory: 4k page, physical 16306976k(1600884k free), swap 2097148k(2033056k free)
CommandLine flags: -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:MaxNewSize=10485760 -XX:MaxTenuringThreshold=15 -XX:NewSize=10485760 -XX:OldPLABSize=16 -XX:PretenureSizeThreshold=3145728 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
0.106: [GC (Allocation Failure) 0.106: [ParNew (promotion failed): 7821K->8371K(9216K), 0.0037815 secs]0.110: [CMS: 8194K->6670K(10240K), 0.0034083 secs] 11917K->6670K(19456K), [Metaspace: 3190K->3190K(1056768K)], 0.0073212 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
Heap
par new generation total 9216K, used 2130K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 26% used [0x00000000fec00000, 0x00000000fee14930, 0x00000000ff400000)
from space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
concurrent mark-sweep generation total 10240K, used 6670K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
Metaspace used 3197K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 343K, capacity 388K, committed 512K, reserved 1048576K
byte[] array1 = new byte[4 * 1024 * 1024];
array1 = null;
直接分配 4MB 的大对象,此时这个对象会直接进入老年代,array1
不再引用这个数组。
byte[] array2 = new byte[2 * 1024 * 1024];
byte[] array3 = new byte[2 * 1024 * 1024];
byte[] array4 = new byte[2 * 1024 * 1024];
byte[] array5 = new byte[128 * 1024];
连续分配了 4个数组,其中3个是 2MB的数组,1个是 128KB的数组,全部进入Eden
区域。
byte[] array6 = new byte[2 * 1024 * 1024];
在(2)的基础上,再执行此行代码。
Eden
区,已经放不下 2MB 的对象了,因此会直接触发一次 Young GC
GC日志如下:
0.106: [GC (Allocation Failure) 0.106: [ParNew (promotion failed): 7821K->8371K(9216K), 0.0037815 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
日志解释:
Eden
区已经有 7821KB 的对象,但是回收之后发现一个都回收不了,因为均引用了。
这时候,会把这些对象(3个2MB、1个128KB)直接放入老年代,但这样就超过老年代的10MB的大小了,所以会有如下 gc日志:
0.110: [CMS: 8194K->6670K(10240K), 0.0034083 secs] 11917K->6670K(19456K), [Metaspace: 3190K->3190K(1056768K)], 0.0073212 secs]
此时执行了 CMS
垃圾回收器的 FULL GC
。
full gc:
- 对老年代进行
old gc
- 跟
young gc
进行关联- 触发一次元数据区(永久代)的
GC
-》metaspace
young gc
,把2个2MB的数组放入老年代如图:
CMS
的FULL GC
回收掉老年代的 4MB的数组,因为他已经没人引用了。
老年代中放入 1个 2MB 和 1个128KB的数组,如图:
CMS FULL GC
执行完毕后,新生代的对象都进入老年代。此时在 新生代分配 2MB的数组就成功了
如图: