jvisualvm 插件_JVM(Java虚拟机)监控利器jvisualvm的使用

对于从事C、C++程序开发的开发人员来说,在内存管理领域,他们拥有最高权力,需要担负每一个对象生命开始到终结的维护责任,对于不在使用的对象要及时的delete/free掉。

而对于Java程序员来说,在JVM的自动内存管理机制的帮助下,不再需要为每一个对象的new操作去写配对的delete/free代码,不容易出现内存泄漏和内存溢出问题,不过,也正是因为Java程序员把内存控制的权力交给了Java虚 拟机,一旦出现内存泄漏和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,那么排查错误将会成为一项异常艰难的工作。

在介绍jvisualvm之前,先来了解下虚拟机内存结构:

2f169feea456f890272086c8cd9444ad.png

其中新生代(YoungGeneration)和老年代(OldGeneration)在堆中默认的分配比例是1:2。对于新生代,伊甸区(EdenSpace),幸存区1(FromSpace),幸存区2(ToSpace)的默认分配比例是8:1:1。而永久代(PermGen)在JDK8中已被元空间(Metaspace)取代。

jvisualvm是到目前为止随JDK发布的功能最 强大的运行监视和故障处理程序,并且可以预见在未来一段时间内都是官方主力发展的虚拟机故障处理工具。它不需要被监视的程序基于特殊Agent运行,因此它对应用程序的实际性能的影响很小,使得它可以直接应用在生产环境中。

jvisualvm基于NetBeans平台开发,因此它一开始就具备了插件扩展功能的特性,通 过插件扩展支持,jvisualvm可以做到:

  • 显示虚拟机进程以及进程的配置、环境信息(jps、jinfo)。
  • 监视应用程序的CPU、GC、堆、方法区以及线程的信息(jstat、jstack)。
  • dump以及分析堆转储快照(jmap、jhat)。
  • 方法级的程序运行性能分析,找出被调用最多、运行时间最长的方法。
  • 离线程序快照:收集程序的运行时配置、线程dump、内存dump等信息建立一个快
  • 照,可以将快照发送开发者处进行Bug反馈。
  • 其他plugins的无限的可能性……

可以看到Java应用的CPU、堆、线程等的使用情况:

2cd0f5b400717221c4981ace807bb623.png

通过安装插件,还可以更具体的看到新生代、老年代的使用情况:

bead383a1b9eeb2837103d790512e8cb.png

分析应用性能

在Profiler页签中,jvisualvm提供了程序运行期间方法级的CPU执行时间分析以及内存分析,做Profiling分析肯定会对程序运行性能有比较大的影响,所以一般不在生产环境中使用这项功能。要开始分析,先选择"CPU"和“内存”按钮中的一个,然后切换到应用程序中对程序进行操作,VisualVM会记录到这段时间中应用程序执行过的方法。如果是CPU分析,将会统计每个方法的执行次数、执行耗时;如果是内存分析,则会统计每个方法关联的对象数以及这些对象所占的空间。

16f12e02ccc8019ea6c727c8a9e4713e.png

66d77e2c0afb20bdca5f8b8057692c90.png

这里还可以利用OQL来查询检索对象:

9d31697fdd2e964cf5635404c13e7ed6.png

更多的功能大家可以自己去探索。

关于jvisualvm的堆分析,我通过一个实际案例来说明。有一个SpringBoot的微服务请求响应缓慢,通过jstat命令查看内存和gc情况:

21ffc8011ba6888e3bc5bfc11e9a1356.png

可以看到,新生代和老年代的空间基本都已被用光,minor GC和Full GC频繁,为了保证服务的可用性,使用jmap命令导出堆信息后,就重启了服务。

接下来为了找到堆被占满的原因,到底是内存泄漏还是内存溢出,我们通过jvisualvm来一探究竟。将堆文件从服务器下载到本地,使用jvisualvm打开,可以看到插入char[]类型对象占据了八百多兆空间,这很不正常,为何有这么多char[]对象没能被回收:

efa306445caf3a6b9749595771c23c55.png

双击char[]进入实例数页签,随机找到一个对象,右键选择显示最近的垃圾回收根节点:

2dee8d4712e486ac116165b6d31c200e.png

可以看到,是被hibernate的EntityEntryContext$EntityEntryCrossRefImpl[]的这么个数组对象引用着,在EntityEntryContext$EntityEntryCrossRefImpl[]这里再右键选择在线程中显示,一个状态为Runnable的线程正在使用这个对象:

bc110268482d19cafad5997e63207550.png

经过这样反复分析,可以发现有数十个线程正在执行这个同样的工作,即意味着有数十个EntityEntryContext$EntityEntryCrossRefImpl[]的数组对象,这数十个EntityEntryContext$EntityEntryCrossRefImpl[]的数组对象里有差不多上百万个EntityEntryContext$EntityEntryCrossRefImpl对象,它们间接引用的char[]、String等对象达到了数千万个,由于这些对象都是有效存活的,所以虚拟机每次gc均不能回收它们,也就导致了服务请求响应缓慢。

最后找到问题根源,是一个定时任务导致的,该定时任务每分钟执行一次,平时情况下该定时任务一般很快能执行完,但在特殊活动期间服务访问量剧增,而代码又没控制每次批处理的对象数量,所以定时任务执行时间被拉得很长,导致越来越多的线程被此定时任务占用。

所以遇到问题别慌,灵活使用jps、jstat、jstack等命令,检查JVM的CPU、内存、gc等情况,使用jvisualvm分析堆情况,就能找到问题根源并给予解决。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值