《深入理解JVM虚拟机》 - VM性能分析工具与案例分析

JVM性能问题的处理依据:运行日志与异常堆栈、GC日志(printGCDetails)、线程快照(threaddump/javacore)、堆转储快照(heapdump)。

一、工具

1. JDK命令行工具

1)jps:process status,输出进程ID,main class;提供多个参数:-v输出虚拟机启动参数;-l输出主类的全名/jar包的地址;-m输出main函数接收的参数

2)jinfo:用于查看、调整VM配置。jps -v只查看显式指定的VM参数,但jinfo查看所有参数,并修改允许运行时修改的参数。jinfo pid;jinfo -flag <flagName> <newFlagValue>。


3)jstat:显示VM运行数据statistics。服务器上定位VM性能问题的首选,但可视化不及VisualVM。jstat-<option> vmid interval count,如jstat-gc 2764 250 20表示每250ms查看一次2764进程的GC情况,共20次。提供option包括1)class:监测类加载、卸载;2)gc:监测Eden、Survivor、老年代、永久代的容量、已用空间、GC时间;3)gccause:上次GC原因;4)compile:输出JIT编译过的方法,耗时。


4)jmap:提供option包括:1)dump:生成heapdump;2)finalizerinfo:查询finalise执行队列;3)heap:显示堆信息。

5)jhat:heap analysis tool,分析heapdump并用html展示结果。分析工作耗时耗CPU,通常拷贝到其他机器用visual VM等工具分析,而不是在运行应用服务器用jhat分析。相对于Visual VM,jhat功能简陋,Intellij没有捆绑的分析工具。

6)jstack:线程栈快照,分析线程长时间停顿原因,如死锁/死循环/请求外部资源等待。参数:-F无响应时强制输出线程栈;-l锁的附加信息;-m本地方法栈;Thread类的getAllStackTrace可实现管理员页面,通过浏览器查看线程堆栈。

2. JDK的可视化工具

1)Jconsole:运行jdk/bin下面的jconsole.exe启动Jconsole,自动搜索虚拟机进程,双击某进程进行监控,支持监控远程虚拟机;展示堆使用情况,线程数目等。

2)VisualVM(主流):基于插件可扩展,显示进程/进程配置信息,生成和浏览heapdump。性能分析profiler:运行时分析方法级的CPU执行时间和内存,对程序运行效率影响大,通常不用于生产环境;CPU分析统计每个方法的执行次数/耗时;内存分析统计每个方法关联的对象数和对象所占空间。BTrace:在不停止目标程序运行的前提下,动态加入原本不存在的调试代码

二、调优案例实战与分析

1. 高性能硬件上的服务部署策略🌟

问题:在线文档网站,原有硬件1.5G堆内存,用户感觉使用缓慢;进行了硬件升级16G内存,12G堆内存,反而明显卡顿;系统监控显示经常出现长时间失去响应的情况。

原因:使用吞吐量优先的收集器,频繁发生Full GC,每次耗时14秒;频繁Full GC的原因:文档反序列化生成的大对象未被minor GC回收,直接进老年代导致老年代很快满载

解决方式:1. 通过64位JDK使用大内存,用户交互型(时间敏感)应用必须控制full GC次数(如每天一次),深夜定时触发full GC。控制full GC次数的关键是多数对象(尤其大对象)符合朝生夕死,以保障老年代的稳定。网站应用中对象的生命周期多是请求/页面级,代码合理情况下能保证Full GC的频率。当前64位JDK的问题在于:性能低于32位,堆溢出无法生成dump文件,或dump文件过大难以分析。2. 若干32位虚拟机建立逻辑集群,在物理机启动多个应用进程,分配不同端口,通过负载均衡器分配访问请求;问题:容易出现资源竞争,比如磁盘资源竞争引起的IO异常。

最终解决方案:调整为4个32位JDK的逻辑集群;使用CMS收集器

2. 集群间同步导致的内存溢出

问题:MIS系统;硬件是两台小型机,每个机器部署3个实例,共享数据存储在数据库和全局缓存。全局缓存启用后,服务正常使用了一段时间,最近不定期出现内存溢出。

排查原因:监测GC,发现内存不溢出时GC运行合理:每次GC后内存都回到合理水平;没有代码更新,排除代码引起的内存泄漏。让应用带HeapDumpOnOutOfMemoryError参数执行,管理员拿到最近内存溢出后的heapdump文件,发现大量NAKACK对象,该对象服务于全局缓存。问题一部分在于JBossCache本身的缺陷(论坛上有讨论,并且后续有版本更新),另一部分在于MIS系统实现方式的缺陷(使用全局缓存时可以有频繁的读操作,但不适合有频繁的写操作)

3. 堆外内存导致的溢出🌟

问题&排查:基于B/S架构的电子考试系统;测试发现服务器不定时抛出OOM异常,管理员把内存调大后OOM异常更频繁;加入HeapDumpOnOutOfMemory参数,没有输出;运行jstat,堆/方法区内存压力不大。日志中错误堆栈:OOM-null,直接内存溢出。

原因:除了堆内存,直接内存/栈内存/socket缓存区/JNI代码/虚拟机和GC也需要占用内存,本例中硬件2G内存,堆内存1.6G,那堆外内存<0.4G;不同于新生代/老年代,直接内存不足时不会通知收集器进行GC,只能等待Full GC发生时顺带清理直接内存,否则内存溢出并在catch语句中调用system.gc触发full GC,如果虚拟机打开了DisableExplicitGC,gg。

Socket缓存区:每个socket连接都需要receive和send两个缓存区,连接数过多也会OOM:Too many open files;JNI用于调用本地库;虚拟机和GC执行占用内存。

4. 外部命令导致系统缓慢

问题:数字校园系统,压力测试发现响应慢,监控发现CPU占用率高,且占用CPU多数资源的并非应用本身(应用的CPU利用率应占主要地位)。

排查:通过Dtrace发现fork大量占用CPU,linux用fork产生新进程。检查代码大量调用Runtime.getRuntime().exec()执行外部shell脚本获取系统信息,它会克隆一个和当前虚拟机拥有相同环境变量的进程,用于执行外部命令,最后退出进程;频繁调用产生的创建进程的开销大。

5. 服务器JVM进程崩溃

问题:MIS系统,硬件是2个HP系统(2个CPU,8G内存),正常运行一段时间后,频繁出现虚拟机进程自动关闭。

排查:崩溃前错误日志包含大量connection reset远程断开链接异常。该系统最近与另一个系统A集成,通过soapui调用系统A,发现要3分钟才能响应,且经常出现断开连接。MIS系统虽然采用异步方式调用系统A,但速度完全不对等导致等待的线程和socket连接越来越多,最终导致虚拟机崩溃。

解决:通知A系统修复无法使用的接口;异步调用改为生产者/消费者模式的消息队列。

6. 不恰当数据结构导致内存占用过大

问题:后台服务器,采用ParNew+CMS收集器,平时minorGC只需要30ms,但业务上需要每10分钟加载一个80M的文件进行处理,在内存中形成100万个HashMap entry,这段时间的Minor GC会造成0.5s停顿。因为加载文件时新生代迅速填满从而引发Minor GC,同时对象仍然存活引发大量拷贝。

解决方案:1.不修改程序,仅从GC调优的角度解决:去掉survivor,新生代的存活对象直接进入老年代。2.治本的方案是修改代码,不用hashMap存储数据。hashmap存储数据空间效率低,HashMap中key和value是有效数据,两个长整型共16字节; 长整型包装为Long需要叠加8B的Markword,8B的Klass指针,膨胀为24字节。两个Long组成hashmap entry还需要16B的对象头,8B的next字段,4B的hash字段,和为了对齐的4B填充,以及hashMap对entry的8B引用,实际消耗内存88B。空间利用率16/88

三、Eclipse启动速度调优

启动时间包含:GC4.2秒,full GC19次,共3.1s;minorGC 378次,共1.1s;类加载:9115个,4.1s;JIT编译时间:2s,由后台线程完成。

如何优化:

1)虚拟机版本升级,希望从虚拟机本身得到免费的性能提升。升级之后意外出现OOM,打开visual VM,查看堆曲线和永久代曲线。堆曲线正常:堆大小的曲线和使用的堆的曲线一直存在很大的间隔,每当两条线接近时,最大堆的曲线会快速向上转向(堆扩容),而使用的堆曲线则向下(触发了一次GC)。永久代曲线不正常:永久代大小曲线和使用曲线几乎重合,说明永久代没有可回收的资源,也没有可扩展空间,因此内存溢出是永久代引起的。通过配置MaxPermSize来解决,默认值64M。

2)调整内存设置控制GC频率:Minor GC频繁,新生代空间太小,通过-Xmn设置。Full GC执行效率:回收时只做了扩容,回收效率低,固定堆内存和永久代大小,避免内存自动扩展。Eclipse用户通常选择run in background,CMS适合后台运行,在eclipse.ini文件中加入jvm配置参数。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268