深入JVM内核(三)

JAVA 2015-12-05

今天我们来学习如何获取JVM的信息并对信息进行分析。

首先,要打印JVM的信息,假如你用的是eclipse,选择Windows-Preferences-Java-installed JREs,对jdk进行编辑,在Default VM arguments中加入预定参数并保存,就可以在运行java程序后打印出JVM日志了。如,加上-XX:+PrintGCDetails -Xloggc:C:\gc.log,GC日志保存到C盘根目录。 还有一种方法是修改针对某个可运行类,右键,选择Run As-Run Configurations。选择 Arguments,并添加VM参数,如下图 1.png

运行一个程序,控制台便会输出JVM信息 2.png

下面我们对结果进行分析 在那之前我们先了解几个基本的参数设置:

Trace跟踪参数

  1. -XX:+printGC 打印GC简要信息
  2. -XX:+PrintGCDetails 打印GC详细信息
  3. -XX:+PrintGCTimeStamps 打印GC时间戳
  4. -Xloggc:log/gc.log 指定GC log输出位置
  5. -XX:+PrintHeapAtGC 每次一次GC后,都打印堆信息
  6. -XX:+TraceClassLoading 监控类的加载
  7. -XX:+PrintClassHistogram 按下Ctrl+Break后,打印类的信息: 堆分配参数
  8. -Xmx –Xms -Xmn -Xss 指定最大堆 指定最小堆 指定Java Heap Young区大小 指定每个线程的Stack大小
  9. -XX:SurvivorRatio 设置两个Survivor区和eden的比 10.-XX:+HeapDumpOnOutOfMemoryError OOM时导出堆到文件 11.-XX:+HeapDumpPath 导出OOM的路径 12.-XX:OnOutOfMemoryError 在OOM时,执行一个脚本 13.-Xss 通常只有几百K 当然参数不止这些,以上只是常用的,权威的还是要看官方文档 参考文档

    下面我们就用这些命令进行分析: 首先来看-XX:+printGC的输出

    [GC 4790K->374K(15872K), 0.0001606 secs] [GC 4790K->374K(15872K), 0.0001474 secs] [GC 4790K->374K(15872K), 0.0001563 secs] [GC 4790K->374K(15872K), 0.0001682 secs]

    表示回收的空间以及花费的时间

    接着来看-XX:+PrintGCDetails的输出

    Heap PSYoungGen total 38400K, used 3328K [0x00000000d5f00000, 0x00000000d8980000, 0x0000000100000000) eden space 33280K, 10% used [0x00000000d5f00000,0x00000000d6240278,0x00000000d7f80000) from space 5120K, 0% used [0x00000000d8480000,0x00000000d8480000,0x00000000d8980000) to space 5120K, 0% used [0x00000000d7f80000,0x00000000d7f80000,0x00000000d8480000) ParOldGen total 87552K, used 0K [0x0000000081c00000, 0x0000000087180000, 0x00000000d5f00000) object space 87552K, 0% used [0x0000000081c00000,0x0000000081c00000,0x0000000087180000) Metaspace used 2648K, capacity 4486K, committed 4864K, reserved 1056768K class space used 286K, capacity 386K, committed 512K, reserved 1048576K

    Heap可以分为两种,PSYoungGen(新生代)ParOldGen(老年代),Metaspace(元空间,是jdk8才引入的,jdk8之前有个PermGen(永久代))。可以看出新生代中eden space是出生带,已经使用了10%,而幸存带from=to。 注:年老代存放从年轻代存活的对象。一般来说年老代存放的都是生命期较长的对象。 最后的地址值分别表示低边界,当前边界和最高边界,它们的意思分别是在内存中的起始位置,当前位置和所能到达的最高位置。

    运行-XX:+TraceClassLoading,类加载输出得到类使用情况 3.png

    下面我们针对堆进行实验: 用Xmx20m -Xms5m分配堆空间后执行

    public class Test { public static void main(String[] args) { byte[] b=new byte[110241024]; System.out.print("Xmx="); System.out.println(Runtime.getRuntime().maxMemory()/1024.0/1024+"M"); System.out.print("free mem="); System.out.println(Runtime.getRuntime().freeMemory()/1024.0/1024+"M"); System.out.print("total mem="); System.out.println(Runtime.getRuntime().totalMemory()/1024.0/1024+"M"); } }

    代码运行,输出空间使用情况 Xmx=18.0M free mem=3.6919708251953125M total mem=5.5M

    如果byte[] b=new byte[410241024]; 输出 Xmx=18.0M free mem=5.192070007324219M total mem=10.0M

    显然内存增多,但是如果分配内存后执行System.gc();

    public class Test { public static void main(String[] args) { //byte[] b=new byte[410241024]; System.gc(); System.out.print("Xmx="); System.out.println(Runtime.getRuntime().maxMemory()/1024.0/1024+"M"); System.out.print("free mem="); System.out.println(Runtime.getRuntime().freeMemory()/1024.0/1024+"M"); System.out.print("total mem="); System.out.println(Runtime.getRuntime().totalMemory()/1024.0/1024+"M"); } }

    空闲内存增多,输出 Xmx=18.0M free mem=4.918022155761719M total mem=5.5M

    所谓JVM调优就是可以根据对参数的设置提高虚拟机的效率并节省内存,其它的大局可以一一动手实践,不一一写出。 总结:可以根据实际事情调整新生代和幸存代的大小 官方推荐新生代占堆的3/8 幸存代占新生代的1/10 在OOM时,记得Dump出堆,确保可以排查问题


本文由 Tony 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

如果对您有用,您的支持将鼓励我继续创作!