测试代码
测试代码模拟了业务系统在单位时间内不断的创建对象,然后部分对象被回收,部分对象放入老年代。我们就可以通过打印GC日志查看不同GC垃圾回收器下的堆内存状态。
/*
演示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;
}
}
串行GC
堆内存256MB
java -XX:+UseSerialGC -Xms256m -Xmx256m -XX:+PrintGCDetails GCLogAnalysis.java
- 9个youngGC,之后一直fullGC
- youngGC在10毫秒左右,fullGC在10毫秒左右
- 内存不够,出现OOM
堆内存512MB
java -XX:+UseSerialGC -Xms512m -Xmx512m -XX:+PrintGCDetails GCLogAnalysis
- 多次youngGC,1次fullGC
- youngGC在15毫秒左右,fullGC50毫秒
- 生成对象9727
堆内存1GB
java -XX:+UseSerialGC -Xms1g -Xmx1g -XX:+PrintGCDetails GCLogAnalysis
- 多次youngGC
- youngGC在30毫秒左右
- 生成对象14916
堆内存4GB
java -XX:+UseSerialGC -Xms4g -Xmx4g -XX:+PrintGCDetails GCLogAnalysis
- 只进行了3次youngGC
- youngGC在80毫秒左右
- 生成对象13590
总结:
- 堆内存越大,执行youngGC和fullGC的次数越少
- 单次youngGC和fullGC的时间随着内存的增大而增大
- 在堆内存为1g时,生成的对象最多
采用串行GC,随着堆内存的增大,GC的次数会减少,但是每次GC的时间会增加。
并行GC
堆内存256MB
java -XX:+UseParallelGC -Xms256m -Xmx256m -XX:+PrintGCDetails GCLogAnalysis.java
- 10个youngGC,之后一直fullGC
- youngGC在4毫秒左右,fullGC20毫秒左右
- 内存不够,出现OOM
堆内存512MB
java -XX:+UseParallelGC -Xms512m -Xmx512m -XX:+PrintGCDetails GCLogAnalysis
- 多次youngGC,多次fullGC
- youngGC在8毫秒左右,fullGC42毫秒左右
- 生成对象8281
堆内存1GB
java -XX:+UseParallelGC -Xms1g -Xmx1g -XX:+PrintGCDetails GCLogAnalysis
- 多次youngGC一次fullGC
- youngGC在15毫秒左右,fullGC50毫秒
- 生成对象15066
堆内存4GB
java -XX:+UseParallelGC -Xms4g -Xmx4g -XX:+PrintGCDetails GCLogAnalysis
- 只进行了4次youngGC
- youngGC在38毫秒左右
- 生成对象17035
总结:
- 堆内存越大,执行youngGC和fullGC的次数越少
- 单次youngGC和fullGC的时间随着内存的增大而增大
- 在堆内存为越大,生成对象越多
- 采用并行GC比串行GC每次GC的时间更少
CMSGC
堆内存256MB
java -XX:+UseConcMarkSweepGC -Xms256m -Xmx256m -XX:+PrintGCDetails GCLogAnalysis.java
- 9个youngGC,
- youngGC在10毫秒左右,fullGC在30毫秒左右
- 生成对象4329
堆内存512MB
java -XX:+UseConcMarkSweepGC -Xms512m -Xmx512m -XX:+PrintGCDetails GCLogAnalysis
- 多次youngGC,多次fullGC
- youngGC在12毫秒左右,fullGC在57毫秒左右
- 生成对象11254
堆内存1GB
java -XX:+UseConcMarkSweepGC -Xms1g -Xmx1g -XX:+PrintGCDetails GCLogAnalysis
- 多次youngGC,多次fullGC
- youngGC在15毫秒左右,fullGC在60毫秒左右
- 生成对象15820
堆内存4GB
java -XX:+UseConcMarkSweepGC -Xms4g -Xmx4g -XX:+PrintGCDetails GCLogAnalysis
- 只进行了6次youngGC
- youngGC在50毫秒左右
- 生成对象15145
总结:
- 堆内存越大,执行youngGC和fullGC的次数越少
- 单次youngGC和fullGC的时间随着内存的增大而增大
- 在堆内存为越大,生成对象越多
G1GC
堆内存256MB
java -XX:+UseG1GC -Xms256m -Xmx256m -XX:+PrintGCDetails GCLogAnalysis.java
- 内存不够,出现OOM
- GC时间1.6ms左右
堆内存512MB
java -XX:+UseG1GC -Xms512m -Xmx512m -XX:+PrintGCDetails GCLogAnalysis
- GC时间1.3ms左右
- 生成对象11284
堆内存1GB
java -XX:+UseG1GC -Xms1g -Xmx1g -XX:+PrintGCDetails GCLogAnalysis
- GC时间3.2ms左右
- 生成对象14569
堆内存4GB
java -XX:+UseG1GC -Xms4g -Xmx4g -XX:+PrintGCDetails GCLogAnalysis
- GC时间8.7ms左右
- 生成对象16853
总结:
- 堆内存越大,越适合用G1GC
- 在堆内存为越大,生成对象越多
如何选择GC
选择正确的GC算法,唯一可行的方式就是去尝试,一般性的指导原则:
- 如果系统考虑吞吐优先,CPU资源都用来最大程度处理业务,用Parallel GC。
- 如果系统考虑低延迟有限,每次GC时间尽量短,用CMS GC。
- 如果系统内存堆较大,同时希望整体来看平均GC时间可控,使用G1 GC。
对于内存大小的考量:
- 一般4G以上,算是比较大,用G1的性价比较高。
- 一般超过8G,比如16G-64G内存,非常推荐使用G1 GC。