对于不同 GC 和堆内存测试笔记

94 阅读5分钟

对于不同 GC 和堆内存的测试笔记

测试代码


import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAdder;
/*
演示GC日志生成与解读
*/
public class GCLogAnalysis {
    private static Random random = new Random();
    public static void main(String[] args) {
        // 当前毫秒时间戳
        long startMillis = System.currentTimeMillis();
        // 持续运行毫秒数; 可根据需要进行修改
        long timeoutMillis = TimeUnit.SECONDS.toMillis(1);
        // 结束时间戳
        long endMillis = startMillis + timeoutMillis;
        LongAdder counter = new LongAdder();
        System.out.println("正在执行...");
        // 缓存一部分对象; 进入老年代
        int cacheSize = 2000;
        Object[] cachedGarbage = new Object[cacheSize];
        // 在此时间范围内,持续循环
        while (System.currentTimeMillis() < endMillis) {
            // 生成垃圾对象
            Object garbage = generateGarbage(100*1024);
            counter.increment();
            int randomIndex = random.nextInt(2 * cacheSize);
            if (randomIndex < cacheSize) {
                cachedGarbage[randomIndex] = garbage;
            }
        }
        System.out.println("执行结束!共生成对象次数:" + counter.longValue());
    }

    // 生成对象
    private static Object generateGarbage(int max) {
        int randomSize = random.nextInt(max);
        int type = randomSize % 4;
        Object result = null;
        switch (type) {
            case 0:
                result = new int[randomSize];
                break;
            case 1:
                result = new byte[randomSize];
                break;
            case 2:
                result = new double[randomSize];
                break;
            default:
                StringBuilder builder = new StringBuilder();
                String randomString = "randomString-Anything";
                while (builder.length() < randomSize) {
                    builder.append(randomString);
                    builder.append(max);
                    builder.append(randomSize);
                }
                result = builder.toString();
                break;
        }
        return result;
    }
}

本人环境
jdk11
mac os 11.x系统

以下测试基本每个参数测试次数在15~20次左右,基本近似取平均值,可能存在误差

不同机器测试可能存在误差,但大体不差;

分别测试了从128 256 512 1g 2g 到 4g 不通垃圾回收器下创建对象数量以及full gc时间大小对比

垃圾回收器

串行垃圾回收

java -XX:+UseSerialGC -Xms128m -Xmx128m -XX:+PrintGCDetails GCLogAnalysis

oom young gc 时间大概在 2-3ms左右 , full gc时间大概 2ms ~ 10ms 左右 不等

java -XX:+UseSerialGC -Xms256m -Xmx256m -XX:+PrintGCDetails GCLogAnalysis

oom young gc 时间大概在 10ms左右 , full gc时间大概 2ms ~ 15ms 左右 不等

java -XX:+UseSerialGC -Xms512m -Xmx512m -XX:+PrintGCDetails GCLogAnalysis

大概创建对象数量在1W4左右 young gc 时间大概在 30ms左右 , full gc时间大概 30ms

java -XX:+UseSerialGC -Xms1g -Xmx1g -XX:+PrintGCDetails GCLogAnalysis

大概创建对象数量在1W4 到 近1w6之间 young gc 时间大概在 40 ~ 70 ms 不等 , full gc时间…

java -XX:+UseSerialGC -Xms2g -Xmx2g -XX:+PrintGCDetails GCLogAnalysis

大概创建对象数量在 1w3左右 young gc 时间大概在 90-120ms 不等 , full gc时间…

java -XX:+UseSerialGC -Xms4g -Xmx4g -XX:+PrintGCDetails GCLogAnalysis

大概创建对象数量在 1w左右 young gc 时间大概在 140-170ms 不等 , full gc时间…

总结

可以看到在堆内存越来越大时候,创建对象数量先变大大概在1g的时候数量是最多的,young gc次数也慢慢变多
gc时间越来越长,几乎是指数上升

并行GC

java -XX:+UseParallelGC -Xms128m -Xmx128m -XX:+PrintGCDetails GCLogAnalysis

oom young gc 时间大概在 2-3ms左右 , full gc时间大概 2ms ~ 7ms 左右 不等

java -XX:+UseParallelGC -Xms256m -Xmx256m -XX:+PrintGCDetails GCLogAnalysis

oom young gc 时间大概在 1-10ms左右 , full gc时间大概 7ms ~ 15ms 左右 不等

java -XX:+UseParallelGC -Xms512m -Xmx512m -XX:+PrintGCDetails GCLogAnalysis

大概创建对象数量在1W2 - 1w4左右 young gc 时间大概在 5ms左右 , full gc时间大概 15-28ms

java -XX:+UseParallelGC -Xms1g -Xmx1g -XX:+PrintGCDetails GCLogAnalysis

大概创建对象数量在1W7左右 young gc 时间大概在 5-9ms ,偶尔 10+ms 不等 , full gc时间 30ms内

java -XX:+UseParallelGC -Xms2g -Xmx2g -XX:+PrintGCDetails GCLogAnalysis

大概创建对象数量在 1w6左右 young gc 时间大概在 10-50ms 不等 , full gc时间…

java -XX:+UseParallelGC -Xms4g -Xmx4g -XX:+PrintGCDetails GCLogAnalysis

大概创建对象数量在 1w-1.2w左右 young gc 时间大概在 100m左右 , full gc时间…

总结

创建对象数量随着内存的大小 逐渐变大进而变小
gc时间在堆内存大小1g开始时候 垃圾回收时间基本是串行回收期的一般时间

CMS

java -XX:+UseConcMarkSweepGC -Xms128m -Xmx128m -XX:+PrintGCDetails GCLogAnalysis

oom young gc 时间大概在 5-10ms左右 , full gc时间大概 10ms ~ 20ms 左右 不等

java -XX:+UseConcMarkSweepGC -Xms256m -Xmx256m -XX:+PrintGCDetails GCLogAnalysis

oom young gc 时间大概在 10-20+ms左右 , full gc时间大概 20ms ~ 30ms 左右 不等

java -XX:+UseConcMarkSweepGC -Xms512m -Xmx512m -XX:+PrintGCDetails GCLogAnalysis

大概创建对象数量在 1w4左右 young gc 时间大概在 20-40ms左右 , full gc时间大概 20-40ms

java -XX:+UseConcMarkSweepGC -Xms1g -Xmx1g -XX:+PrintGCDetails GCLogAnalysis

大概创建对象数量在1W4左右 young gc 时间大概在 30-50ms , full gc时间 …

java -XX:+UseConcMarkSweepGC -Xms2g -Xmx2g -XX:+PrintGCDetails GCLogAnalysis

大概创建对象数量在 1w2左右 young gc 时间大概在 60-100ms 不等 , full gc时间…

java -XX:+UseConcMarkSweepGC -Xms4g -Xmx4g -XX:+PrintGCDetails GCLogAnalysis

大概创建对象数量在 1.2w左右 young gc 时间大概在 60-100m左右 , full gc时间…

总结

可以看到 cms创建对象数量不是很多,吞吐量不如并发甚至串行收集器,垃圾回收时间与并行回收相差无异。

G1

java -XX:+UseG1GC -Xms128m -Xmx128m -XX:+PrintGC GCLogAnalysis

oom young gc 时间大概在 0.1~0.2ms左右 , full gc时间大概 1ms ~ 2ms 左右 不等

但gc次数达50~110次不等

java -XX:+UseG1GC -Xms256m -Xmx256m -XX:+PrintGC GCLogAnalysis

oom young gc 时间大概在 0.5ms左右 , full gc时间大概 1ms ~ 2ms 左右 不等

gc次数达 100~200次不等

java -XX:+UseG1GC -Xms512m -Xmx512m -XX:+PrintGC GCLogAnalysis

大概创建对象数量在 1w6左右 young gc 时间大概在 0.5-0.7ms左右, full gc时间大概 1 ~ 2ms

gc次数达 170~200次不等

java -XX:+UseG1GC -Xms1g -Xmx1g -XX:+PrintGC GCLogAnalysis

大概创建对象数量在2.1W左右 young gc 时间大概在 0.5-3ms,个别 10+ms full gc时间 …

gc次数达 50次左右

java -XX:+UseG1GC -Xms2g -Xmx2g -XX:+PrintGC GCLogAnalysis

大概创建对象数量在 1.6-1.8w左右 young gc 时间大概在 10+ms ~ 40ms 不等 , full gc时间…

gc次数达 10~40次左右

java -XX:+UseG1GC -Xms4g -Xmx4g -XX:+PrintGC GCLogAnalysis

大概创建对象数量在 1.6w左右 young gc 时间大概在 20+ms ~ 50ms 不等,个别90ms , full gc时间…

gc次数达 10~20次左右

总结

1 从 128m到256m 由于对大小小于创建对象占用的大小,导致oom,此种情况下;内存越小不管是yong gc 和 full gc次数会频繁触发,导致大量时间在做gc;
2 同等内存大小情况下 串行gc 时间 几乎是 并行gc的二倍;
3 内存越大的情况下 G1 回收的效率越高,gc时间比其他回收器时间短、内存越大g1效果越好;
4 可以看到内存越大的情况下g1的垃圾回收基本优于串行 并行 以及 cms 不管是从吞吐量和gc时间来说;
5 cms 于 吞吐量相对于其他回收器明显存在劣势,回收时间不太稳定;