java中的对象都创建在堆中,当堆中的对象所占空间到一定量后,就需要清除掉死亡对象,垃圾回收是JVM自动完成的,不用码农手动清理,但偶尔想看看对象所占的堆空间或GC的具体过程,就需要借助GC日志了。
一、打印GC日志
1、示例程序
程序1
public class GCTest {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
byte[] b = new byte[1*1024*1024];
}
}
}
2、加VM参数 -XX:+PrintGCDetail(以idea为例)
最后记得点击下面的apply和ok,然后运行GCTest,就可以看到:对象都放在eden区48%(10M)的空间,目前没有发生GC,所以Survivor区和年老代都是0%。
3、调整GCTest创建的对象大小为30M
程序2
public class GCTest {
public static void main(String[] args) {
for (int i = 0; i < 30; i++) {
byte[] b = new byte[1*1024*1024];
}
}
}
再次运行,可以看到发生了一次GC,将幸存的对象移至Survivor from区,所以eden、from都有对象存在了。
GC日志:[GC (Allocation Failure) [PSYoungGen: 32978K->1512K(38400K)] 32978K->1520K(125952K), 0.0020363 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
可以看出新生代 YoungGen 分配失败,32978K->1512K(38400K) 表示 GC 前已使用->GC后已使用(该区域总容量),[]之外表示Java堆GC前已使用->GC后已使用(Java堆总容量),最后为 GC 耗时。
4、-XX:+PrintHeapAtGC
-XX:+PrintHeapAtGC的含义是打印GC前后的详细堆栈信息
将它配置在VM options中,运行程序2,就可以看到:
5、-XX:+TraceClassLoading
-XX:+TraceClassLoading的作用是监控类的加载
将它配置在VM options中,运行程序2,就可以看到:
将它配置在VM参数中,运行程序2,就可以看到:
二、堆分配参数
程序3
public class TestMaxMinHeap {
private static final int M = 1024 * 1024;
public static void main(String[] args) {
System.out.println("maxMemory:" + ( Runtime.getRuntime().maxMemory()/M) + "M");
System.out.println("freeMemory:" + (Runtime.getRuntime().freeMemory()/M) + "M");
System.out.println("totalMemory:" + (Runtime.getRuntime().totalMemory()/M) + "M");
}
}
不加任何参数的时候,运行结果是:
1、-Xmx 和 -Xms
-Xmx是最大堆大小,默认值是物理内存的1/4(<1GB),
-Xmx20m 设置 maxMemory 为 20 M,分配空间不能超过最大堆内存大小,否则会抛出 OutofMemory 异常
-Xms是初始堆大小,默认值是物理内存的1/64(<1GB),-Xms20m 设置 total Memory 为 20 M ,totalMemory 就是初始化堆大小,它的意思是一开始限定堆大小为多少,如果不够则可以扩充,但必须小于最大堆大小。
2、测试动态调整
①首先设置 -Xmx20m -Xms5m ,最大堆空间20m,初始大小5m。
②分配2M
程序4
public class TestMaxMinHeap {
private static final int M = 1024 * 1024;
public static void main(String[] args) {
byte b[] = new byte[2 * M];
System.out.println("maxMemory:" + ( Runtime.getRuntime().maxMemory()/M) + "M");
System.out.println("freeMemory:" + (Runtime.getRuntime().freeMemory()/M) + "M");
System.out.println("totalMemory:" + (Runtime.getRuntime().totalMemory()/M) + "M");
}
}
③分配6M
程序5
public class TestMaxMinHeap {
private static final int M = 1024 * 1024;
public static void main(String[] args) {
byte b[] = new byte[6 * M];
System.out.println("maxMemory:" + ( Runtime.getRuntime().maxMemory()/M) + "M");
System.out.println("freeMemory:" + (Runtime.getRuntime().freeMemory()/M) + "M");
System.out.println("totalMemory:" + (Runtime.getRuntime().totalMemory()/M) + "M");
}
}
③分配30M
程序6
public class TestMaxMinHeap {
private static final int M = 1024 * 1024;
public static void main(String[] args) {
byte b[] = new byte[30 * M];
System.out.println("maxMemory:" + ( Runtime.getRuntime().maxMemory()/M) + "M");
System.out.println("freeMemory:" + (Runtime.getRuntime().freeMemory()/M) + "M");
System.out.println("totalMemory:" + (Runtime.getRuntime().totalMemory()/M) + "M");
}
}
2、-Xmn
-Xmn:设置 新生代 大小
①新生代大小设置为 2M,-Xmn2m -Xms20m -Xmx20m -XX:+PrintGCDetails
程序7
public class TestNewSpace {
private static final int M = 1024*1024;
public static void main(String[] args) {
byte[] b = null;
for (int i = 0; i < 10; ++i) {
b = new byte[1*M];
}
}
}
②设置新生代为18M,-Xmn18m -Xms20m -Xmx20m -XX:+PrintGCDetails
3、-SurvivorRatio
-SurvivorRatio:设置新生代中 Eden space 和 Survivor space 的大小
①设置新生代8m,-Xmn8m -Xms20m -Xmx20m -XX:+PrintGCDetails
②将新生代中的的Survivor设置大一点,-Xmn8m -Xms20m -Xmx20m -XX:+PrintGCDetails -XX:SurvivorRatio=2
4、-XX:NewRatio
-XX:NewRatio 设置年老代和年轻代的比值
①设置年老代和年轻代的比例是2:1
-Xms20m -Xmx20m -XX:+PrintGCDetails -XX:NewRatio=2
②设置年老代和年轻代的比例是3:1
-Xms20m -Xmx20m -XX:+PrintGCDetails -XX:NewRatio=3
5、-Xss
-Xss设置每个线程的堆栈大小
①设置线程栈为128K,-Xss128k
程序8
public class TestXssStack {
public static int count = 0;
public static void func() {
count++;
func();
}
public static void main(String[] args) {
try {
func();
}catch (Throwable e){
System.out.println("count:" + count);
}
}
}
②设置线程栈为1m,-Xss1m
总结
本篇主要简单介绍了如何使用idea查看GC日志,以及JVM参数-XX:+PrintGCDetail、-XX:+PrintHeapAtGC、-XX:+TraceClassLoading、-Xmx 、-Xms、-Xmn、-SurvivorRatio、-XX:NewRatio、-Xss所代表的意义和使用。