阈值和垃圾收集器类型对于对象分配的影响实践分析
VM options:
-verbose:gc
//会输出详细的垃圾回收的日志
-Xms20M
//堆容量的初始大小
-Xmx20M
//堆容量的最大大小 -- 两个值一般设置为一样的,这样不会出现抖动的现象。
-Xmn10M
//新生代的大小是10M
-XX:+PrintGCDetails
//打印出垃圾回收详细的信息
-XX:SurvivorRatio=8
//eden和 survivor的比例为8:1的比例。
-XX:PretenureSizeThreshold=4194304 // 大小的阈值 4M的大小 新建的对象大小超过4M直接在老年代生成
-XX:+UseSerialGC
//阈值的使用,需要搭配串行垃圾的收集器 -不添加此参数,上一个参数不起作用。
/**
PretenureSizeThreshold: 设置对象超过多大时,直接在老年代进行分配空间
*/
// 执行前添加上述参数
public class MyTest2 {
public static void main(String[] args) {
int size = 1024 * 1024; // 1M 的容量
byte[] myAlloc1 = new byte[5 * size]; // 创建之后每一个元素都是0
}
}
数组的大小设置为5 和8 和10 的结果各不一样。
'使用之前介绍工具,可以查看当前执行的所有的内存分布情况及垃圾回收情况'
System.gc(): 对JVM进行一次 Full GC 。
MaxtenuringThreshold与阈值的动态调整详解
-verbose:gc
//会输出详细的垃圾回收的日志
-Xms20M
//堆容量的初始大小
-Xmx20M
//堆容量的最大大小 -- 两个值一般设置为一样的,这样不会出现抖动的现象。
-Xmn10M
//新生代的大小是10M
-XX:+PrintGCDetails
//打印出垃圾回收详细的信息
-XX:+PrintCommandLineFlags // 打印命令行的标志,我们自己设置的启动参数
-XX:SurvivorRatio=8
//eden和 survivor的比例为8:1的比例。
-XX:MaxTenuringThreshold=5 //在可以自动调节对象晋升(Promote)到老年代阈值的GC中,设置该阈值的最大值。
-XX:+PrintTenuringDistribution // 打印出年龄为1的字节,年龄为2的字节,等等
/**
MaxtenuringThreshold作用:在可以自动调节对象晋升(Promote)到老年代阈值的GC中,设置该阈值的最大值。该参数的默认值为15,CMS中默认值为6,G1中默认为15(在JVM中,该数值是由4个bit来表示的,1111,即15)
经历了多次GC后,新生代存货的对象会在From Survivor与To Survivor之间来回存放,而这里面的一个前提则是这两个空间有足够的大小来存放这些数据。在GC算法中,会计算每一个对象年龄的大小,如果达到某个年龄后发现总大小已经大于Survivor空间的50%,那么这时候就需要调整阈值,不能再继续等到默认的15次GC后才完成晋升,因为这样会导致Survivor空间不足,所以需要调整阈值,让这些存活对象尽快完成晋升。
*/
// 执行前添加上述参数
public class MyTest3 {
public static void main(String[] args) {
int size = 1024 * 1024; // 1M 的容量
byte[] myAlloc1 = new byte[2 * size];
byte[] myAlloc2 = new byte[2 * size];
byte[] myAlloc3 = new byte[2 * size];
byte[] myAlloc4 = new byte[2 * size];
sout("hello world");
}
}
> 输出结果:
> Task :MyTest3.main()
// PrintCommandLineFlags 打印命令行的标志,我们自己设置的启动参数
-XX:InitialHeapSize=20971520 -XX:InitialTenuringThreshold=5 -XX:MaxHeapSize=20971520 -XX:MaxNewSize=10485760 -XX:MaxTenuringThreshold=5 -XX:NewSize=10485760 -XX:+PrintCommandLineFlags -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
[GC (Allocation Failure)
Desired survivor size 1048576 bytes, new threshold 5 (max 5)
[PSYoungGen: 6815K->560K(9216K)] 6815K->6712K(19456K), 0.0036073 secs] [Times: user=0.04 sys=0.00, real=0.01 secs]
[Full GC (Ergonomics) [PSYoungGen: 560K->0K(9216K)] [ParOldGen: 6152K->6426K(10240K)] 6712K->6426K(19456K), [Metaspace: 2798K->2798K(1056768K)], 0.0032972 secs] [Times: user=0.03 sys=0.00, real=0.00 secs]
hello world
Heap
PSYoungGen
total 9216K, used 2290K [ 0x00000007c0000000, 0x00000007c0000000)
eden space 8192K, 27% used [0x00000007bf600000,0x00000007bf83c8d8,0x00000007bfe00000)
from space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000)
to space 1024K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007c0000000)
ParOldGen
total 10240K, used 6426K [ 0x00000007bf600000, 0x00000007bf600000)
object space 10240K, 62% used [0x00000007bec00000,0x00000007bf246998,0x00000007bf600000)
Metaspace
used 2805K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 291K, capacity 386K, committed 512K, reserved 1048576K
VM options:
-verbose:gc
//会输出详细的垃圾回收的日志
-Xms200M
//堆容量的初始大小
-Xmn50M
//新生代的大小是50M
-XX:TargetSurvivorRatio=60 // 当Survivor空间占据了百分之60,将从新计算阈值。
-XX:+PrintTenuringDistribution // 打印出年龄为1的字节,年龄为2的字节,等等
-XX:+PrintGCDetails
//打印出垃圾回收详细的信息
-XX:+PrintGCDateStamps //打印垃圾回收的时间戳
-XX:+UseConcMarkSweepGC //设置老年代的垃圾回收器
-XX:+UseParNewGC //设置新生代的垃圾回收器
-XX:MaxTenuringThreshold=3 //在可以自动调节对象晋升(Promote)到老年代阈值的GC中,设置该阈值的最大值。
// 综合的例子
public class MyTest4 {
public static void main(String[] args) throws InterruptedException {
byte[] byte_1 = new byte[512 * 1024];
byte[] byte_2 = new byte[512 * 1024];
myGc();
Thread.sleep(1000);
System.out.println("1111111这里进行了垃圾回收");
myGc();
Thread.sleep(1000);
System.out.println("2222222这里进行了垃圾回收");
myGc();
Thread.sleep(1000);
System.out.println("3333333这里进行了垃圾回收");
myGc();
Thread.sleep(1000);
System.out.println("4444444这里进行了垃圾回收");
byte[] byte_3 = new byte[1024 * 1024];
byte[] byte_4 = new byte[1024 * 1024];
byte[] byte_5 = new byte[1024 * 1024];
myGc();
Thread.sleep(1000);
System.out.println("55555555这里进行了垃圾回收");
myGc();
Thread.sleep(1000);
System.out.println("66666666这里进行了垃圾回收");
System.out.println("hello world");
}
private static void myGc(){
// 真正会被收集的数组。方法调用介绍之后就会被垃圾回收。
for(int i = 0 ;i<40;i++){
byte[] byteArray = new byte[1024*1024];
}
}
}
》输出结果:
> Task :MyTest4.main()
2020-02-18T19:44:39.419-0800: [GC (Allocation Failure) 2020-02-18T19:44:39.419-0800: [ParNew
Desired survivor size 3145728 bytes,' new threshold 3 (max 3)
- age 1: 1339912 bytes, 1339912 total
: 40551K使用空间->1361K垃圾回收后大小(46080K总大小), 0.0014325 secs] 40551K->1361K(199680K), 0.0014891 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
//3145728 bytes = 3M 50*0.1*0.6 = 3M
1111111这里进行了垃圾回收
2020-02-18T19:44:40.430-0800: [GC (Allocation Failure) 2020-02-18T19:44:40.430-0800: [ParNew
Desired survivor size 3145728 bytes,' new threshold 3 (max 3)
- age 1:
584 bytes,
584 total
- age 2: 1338144 bytes, 1338728 total
: 42100K->1409K(46080K), 0.0012810 secs] 42100K->1409K(199680K), 0.0013128 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
2222222这里进行了垃圾回收
2020-02-18T19:44:41.435-0800: [GC (Allocation Failure) 2020-02-18T19:44:41.435-0800: [ParNew
Desired survivor size 3145728 bytes,' new threshold 3 (max 3)
- age 1:
72 bytes,
72 total
- age 2:
584 bytes,
656 total
- age 3: 1337920 bytes, 1338576 total
: 41940K->1439K(46080K), 0.0005344 secs] 41940K->1439K(199680K), 0.0005592 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
3333333这里进行了垃圾回收
2020-02-18T19:44:42.441-0800: [GC (Allocation Failure) 2020-02-18T19:44:42.441-0800: [ParNew
Desired survivor size 3145728 bytes, 'new threshold 3 (max 3)
- age 1:
72 bytes,
72 total
- age 2:
72 bytes,
144 total
- age 3:
584 bytes,
728 total
: 42170K->60K(46080K), 0.0027252 secs] 42170K->1397K(199680K), 0.0027533 secs] [Times: user=0.01 sys=0.01, real=0.00 secs]
4444444这里进行了垃圾回收
2020-02-18T19:44:43.454-0800: [GC (Allocation Failure) 2020-02-18T19:44:43.454-0800: [ParNew
Desired survivor size 3145728 bytes, 'new threshold 1 (max 3) // 当前的年龄为1 重新设置了阈值
- age 1: 3145848 bytes, 3145848 total
- age 2:
72 bytes, 3145920 total
- age 3:
72 bytes, 3145992 total
: 40794K->3087K(46080K), 0.0019052 secs] 42131K->4424K(199680K), 0.0019409 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
55555555这里进行了垃圾回收
2020-02-18T19:44:44.465-0800: [GC (Allocation Failure) 2020-02-18T19:44:44.465-0800: [ParNew
Desired survivor size 3145728 bytes,' new threshold 3 (max 3)
- age 1:
80 bytes,
80 total
: 43823K->4K(46080K), 0.0026372 secs] 45160K->4413K(199680K), 0.0026724 secs] [Times: user=0.03 sys=0.00, real=0.00 secs]
66666666这里进行了垃圾回收
hello world
Heap
par new generation total 46080K, used 13912K [0x00000006c3200000, 0x00000006c3200000)
eden space 40960K, 33% used [0x00000006c0000000, 0x00000006c0d95228, 0x00000006c2800000)
from space 5120K, 0% used [0x00000006c2800000, 0x00000006c2801020, 0x00000006c2d00000)
to space 5120K, 0% used [0x00000006c2d00000, 0x00000006c2d00000, 0x00000006c3200000)
concurrent mark-sweep generation total 153600K, used 4409K [0x00000006c3200000, 0x00000006cc800000, 0x00000007c0000000)
Metaspace
used 2806K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 291K, capacity 386K, committed 512K, reserved 1048576K
CMS垃圾回收器(新版本不建议使用)
CMS 垃圾回收器,复杂性比较高。在jdk新版本中不建议被使用。
CMS(Concurrent Mark Sweep):并发标记清除
枚举根节点概念
安全点(Safepoint)概念
程序执行时,并非在所有地方都能停顿下来开始GC,只有在达到安全点时才能暂停。
安全点的选定基本上是以“是否具有让程序长时间执行的特征”为标准来进行选定的。
主动式中断:轮询标志的地方和安全点是重合的。
安全区域(Safe Region)概念
CMS垃圾收集器深入详解
CMS收集器,以获取最短回收停顿时间为目标,多数应用于互联网站或者BS系统的服务器端上。
初始标记的时候,会Stop the world , 此时没有用户线程执行。
并发标记的时候,可以与用户线程同时执行。
重新标记的时候,会Stop the world , 此时也没有用户线程执行。
并发清除的时候,可以与用户线程同时执行。
停顿,停顿的是用户线程停顿的少。
CMS收集器收集步骤
初始标记
并发标记
并发预先清理阶段
并发可丢弃的阶段
最终的重新标记
并发清除
并发重置
出现Concurrent即代表GC线程可以和用户线程同步执行的阶段。
将标记拆分成了前5个阶段。