基于jdk1.8的简单性能监控分析

来源:互联网 发布:删除root软件 编辑:程序博客网 时间:2024/06/04 17:53

jdk的目录下有很多附属的工具,供开发人员跟踪调试使用,对于系统性能监控提供了极大的便利性。虽然也可以通过很多jdk可视化的监控工具进行性能分析,但生产环境比较难以建立起这样的条件,因此了解这些简单的监控命令还是很有必要的。


下面以一个小程序来整理一下堆栈分析的简单步骤。

List<MemoryDemo> list = new ArrayList<MemoryDemo>();while (true) {    list.add(new MemoryDemo());}
  • 运行Java程序:
    java -jar -Xmx32m -Xms32m mebool-demo.jar

  • 运行jps -l(查询Java进程)
    运行 jstat -gcutil 7177 250 20(7177是上面一条命令查询出的进程号,每250毫秒执行一次查询,共查询20次)
    运行当中的后台监控

    这里主要关注的参数有E(Eden区占用比例)、O(Old老年代占用比例)、M(方法区占用比例)。再后面的参数可以参考来推测垃圾回收的繁忙程度:YGC(Young GC次数)、YGCT(Young GC总时间)、FGC(Full GC次数)、FGCT(Full GC总时间)、GCT(GC总时间)。在这里说明一下jdk1.7和jdk1.8的内存模型变化。我们知道在Hotspot虚拟机上,通过永久代来实现方法区,jdk1.8去永久代之后,使用元空间实现方法区。在jdk1.7的环境下执行jstat时,使用P来代表永久代,即方法区的内存占用率,而jdk1.8显示的是M,代表方法区。最后发生内存溢出时的内存监控:

    这里写图片描述

    最后发现老年代接近100%,而发生内存溢出了。jdk1.8默认使用的垃圾收集器Parallel使用的是复制算法,内存处理的机制是优先使用Eden区分配新产生的对象,其次在Survivor1分配对象,为了避免内存出现大量的不连续空间,内存回收时,将Eden区和Survivor1复制到Survivor0的空间中,如果Survivor0的空间不足时,会复制到老年代中,最终进行一次Full GC。因此虽然最后的Eden区虽然有空闲,但繁忙的老年代的空间不足以承受工作压力,最终发生内存溢出。另外,并不是说对象创建的多、空间有限就会导致内存溢出,而是大多数对象到GCRoots的引用一直存在,无法进行垃圾回收导致空间不足而溢出。例如下面的代码基本不会造成内存溢出:
    while (true) {    new MemoryDemo();}
  • 查看堆转储快照
    jmap命令用于生成堆转储快照;jhat命令用于启动堆转储快照的web server,以便于通过界面查看。

    运行命令(13528是jps查询到的进程号)jmap -dump:format=b,file=mebool.bin 13528则当前目录下会生成一个mebool.bin文件,即堆转储快照文件。运行命令jhat mebool.bin浏览器访问 http://localhost:7000,展示的是堆转储快照信息界面:

    这里写图片描述

    第一项package是项目中的包路径及实例列表第二项是实例的统计信息比较实用的是Show heap histogram
    • show heap histogram.

    这里写图片描述

    进入这个列表默认是按照占用空间排序,毋庸置疑的Object类占用的空间最大,再点击instance count,按照生成实例数量统计:

    这里写图片描述

    此时发现我们自己创建的MemoryDemo数量最多,而Total Size却为0。因为它只是实例的一个外壳,真正占用空间的是该实例包含的所有引用对象。而分析内存溢出时,这种数量巨大的实例一般就是罪魁祸首,因此统计数和占用空间要结合起来看。