JAVA应用CPU占用100%|内存泄漏分析总结

2024-08-31 07:58

本文主要是介绍JAVA应用CPU占用100%|内存泄漏分析总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转载自http://www.blogjava.net/ldwblog/archive/2016/12/22/432166.html?opt=admin
JAVA应用CPU占用100%|内存泄漏分析总结

问题现象

vrs后台系统从某一时间点,根据已查结果,追溯到几个月前上线的PGC审核功能引起。近期大概一周左右运营使用时会反馈系统访问越来越来慢,最终系统崩溃,无法访问。因为之前媒资每周都会有功能上线,把这个问题覆盖掉了,未能很快暴露出来。实际上,通过Zabbix观察监控cpu、swap占用都是比较高的。

1031问题分析之CPU 100%

10-31日出现过一次问题,服务器上执行top命令按键1观察始终有一个cpu占用总100%,怀疑可能是因后台服务请求过多CPU繁忙导致访问慢。

将后台服务切到备机,通过堆栈分析具体那段代码引起的CPU占用100%问题。

问题定位过程:

1)jps -m 非常方便直接定位所有的Java进程pid

      [root@cdn ldw]# jps -m | grep 28081

      6687 WatchdogManager start -conf /ldw/conf/resin/resin-mms-webapp-28081.xml --log-directory /ldw/apps/resin/log

      6734 Resin --root-directory /ldw/apps/resin/ -conf /ldw/conf/resin/resin-mms-webapp-28081.xml -server default -socketwait 15304 start --log-directory /ldw/apps/resin/log

2)jstack -l pid > jstack1031.log命令打印栈信息,如果因某些原因无法成功打印,
可以使用kill -3 pid,输出到jvm日志中,resin服务器默认输出到jvm-default.log日志中】
3)查找占用CPU资源最高的进程id,使用top -H -p pid 查看进程pid的所有的线程,默认是按照%CPU高~低排序。
或者直接使用top查看, shift+H显示所有的线程,默认按照%CPU高~低排序。
找到占用CPU利用率最高的pid,一般CPU利用率达到90%以上,将pid转换为16进制,方法有很多种,我使用linux自带python命令:hex(pid),很方便。
4)根据转换的16进制去jstack.log日志中查找基本能定位到具体哪行代码的问题。
"reportThirdException" daemon prio=10 tid=0x00007f8bd450b800 nid=0x12c4 runnable [0x00007f8b906ac000]
java.lang.Thread.State: RUNNABLE
at com.xxx.interfaces.util.NoticeMonitorSysHelper$ThreadStatue$1.run(NoticeMonitorSysHelper.java:167)
at java.lang.Thread.run(Thread.java:722) 

5)fix代码,重新部署上线

      观察cpu迅速降到正常值,swap值也降下来了,第二天观察cpu、swap并没有明显的增加。

6)在备机切换到线上机器前,通过jmap打印出JVM堆内存信息

       jmap -dump:live,format=b,file=heap1031.bin <pid>

 

1212问题分析之内存泄漏

12-11日晚VRS系统又一次服务Down掉,但是当时收到反馈后并没有及时切到备机,未能及时保留问题现场,紧急重启后暂时恢复服务,此时距离上一次上线间隔为9D。

通过Zabbix监控来看swap内存这些天每天都在升高,最大值占用了接近4G,可服务器总内存不过才6G,初步定位肯定是Java应用内存泄漏导致。

第二天一块讨论这块问题如何排查,内存泄漏一般通过jstack输出的栈很难定位到问题,只能对JVM堆内存信息做分析。

这次问题也联想到上次故障处理后,实际并没有找到问题根本原因,想到了1031日存留过JVM堆信息heap1031.bin,然后下载到本地通过MAT(Eclipse插件)进行内存分析。也可以通过其他工具如jhat,但不如MAT直观。

 

问题定位过程:

进入Eclipse:Memory analysis

选择File—》Open Head Dump…打开heap1031.bin

会弹出一个对话框,选择Leak Suspects Report 【自动检测存疑的泄漏点,会报告出那些存活的对象以及这些对象没有被gc的原因】

MAT会自动分析出内存大致情况,直方图显示内存占用以及Problem Suspect

通过以上会看到1635个JPEGImageReader实例没有被释放,可能这个是导致内存泄漏的根源,也说明跟系统裁图功能有关,缩小了问题定位范围。

没有释放资源怀疑可能是IO流没有正常关闭导致,因JVM堆栈转存只看到底层代码,具体还要进一步分析程序源代码。

下一步,点击Details

再点击下面的Class Name,查看Inspector【显示了当前显示类或对象的详细信息】

通过Inspector能知道使用了javax.imageio.ImageReader接口,我们处理图片的任务都在ImageResize.java类中,然后对这块代码进行分析排查。

最初怀疑自动截图这块的影响,单独对自动截图功能做批量测试,循环截图100,1000次dump内存看基本都没有太大变化。

代码排查:

try{

 //ImageReader声称能够解码指定格式  
 Iterator<ImageReader> it = ImageIO.getImageReadersByFormatName(ext);
 ImageReader reader = it.next();
 // 输入源中的图像将只按顺序读取 
 reader.setInput(iis, true);
 int srcWidth = reader.getWidth(0); // 源图宽度
 int srcHeight = reader.getHeight(0); // 源图高度
 if (srcWidth >= destWidth && srcHeight >= destHeight) {

    // 处理图片...

    reader.dispose();

 }

} catch (Exception e) {

  logger.error("Cut image failed, src_image_file:{}, error:{}, ", srcImageFile, e.getMessage(), e);
} finally {

 // ...

}


从ImageReader入手查看代码,发现调用了dispose方法,但是只在try中做了调用,finally中并没有调用dispose方法。怀疑如果if条件不成立或者有异常发生,则不会调用dispose方法。

查看下dispose方法的含义,如果不再使用这个ImageReader对象时,必须调用这个方法释放资源;否则,可能导致资源(内存)无限的被占用。

/**

     * Allows any resources held by this object to be released.  The

     * result of calling any other method (other than

     * <code>finalize</code>) subsequent to a call to this method

     * is undefined.

     *

     * <p>It is important for applications to call this method when they

     * know they will no longer be using this <code>ImageReader</code>.

     * Otherwise, the reader may continue to hold on to resources

     * indefinitely.

     *

     * <p>The default implementation of this method in the superclass does

     * nothing.  Subclass implementations should ensure that all resources,

     * especially native resources, are released.

     */

public void dispose() {

}


找到这个原因时很兴奋,所以我们将try中的reader.dispose()代码注释掉,直接做截图测试,100,1000,然后打印JVM堆栈转存用MAT分析,印证了上面的分析结果。

然后修复这个Java类+log(不符合预期的log打印),部署上线了。

 

因为已知道了JPEGImageReader实例未被释放,故可通过命令jmap -histo:live <pid> | grep ImageReader 【jmap -histo:live <pid> 分析具体的对象数目和占用内存大小】

在线上来查看JPEGImageReader instances数量变化,大概观察1个小时左右,发现JPEGImageReader instances>5了,而且也没有不符合条件的log输出,正常应该释放掉的,难道还是有内存泄漏?

然后,继续分析代码,根据ImageReader搜索了下整个代码库,发现有个PgcAuditServiceImpl.java PGC审核里也有引用的代码。

Iterator<ImageReader> it = ImageIO.getImageReadersByFormatName(ext);
reader = it.next();

//这里没有调用dispose()


这块仅使用了ImageReader获取width和height,之后并没有调用dispose方法,尽快修复重新上线。

持续观察一段时间,jmap查看类及对象情况:

[root@cdn ~]# jmap -histo:live 28093 | grep ImageReader

1905:             1             88  com.sun.imageio.plugins.gif.GIFImageReaderSpi

1913:             1             88  com.sun.imageio.plugins.bmp.BMPImageReaderSpi

1917:             1             88  com.sun.imageio.plugins.wbmp.WBMPImageReaderSpi

1922:             1             88  com.sun.imageio.plugins.png.PNGImageReaderSpi

1924:             1             88  com.sun.imageio.plugins.jpeg.JPEGImageReaderSpi

2685:             2             48  com.sun.imageio.plugins.jpeg.JPEGImageReader$CallBackLock$State

3881:             1             24  [Lcom.sun.imageio.plugins.jpeg.JPEGImageReader$CallBackLock$State;

com.sun.imageio.plugins.jpeg.JPEGImageReader$CallBackLock$State,内部静态类CallBackLock和State,所以有2个instances

[Lcom.sun.imageio.plugins.jpeg.JPEGImageReader$CallBackLock$State; 1个Class对象

持续观察几天性能指标平稳,服务器cpu、swap、load对于内存泄漏前占用都非常少了。

经过以上分析,实际本次故障罪魁祸首是在PGC审核截图引起。香港vrs因运营没有使用到PGC审核功能所以也不会触发内存泄漏问题。

总结

1)Java应用中的CPU 100%、内存泄漏两种排错方式以及具体命令,工具使用上面都已做了说明,可做参考。
2)关注新的监控系统falcon将这些重要服务器性能指标加上监控,超过阈值告警。
3)系统功能上线后,有意识的对服务器性能指标巡逻或许能提前发现异常并提早解决。
4)更多详细MAT使用,网上大量文章可参阅。
5)建议使用Apache Commons Imaging替代Java自身的裁图功能,可以避免显示调用dispose释放资源等问题。

这篇关于JAVA应用CPU占用100%|内存泄漏分析总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1123320

相关文章

使用Java将各种数据写入Excel表格的操作示例

《使用Java将各种数据写入Excel表格的操作示例》在数据处理与管理领域,Excel凭借其强大的功能和广泛的应用,成为了数据存储与展示的重要工具,在Java开发过程中,常常需要将不同类型的数据,本文... 目录前言安装免费Java库1. 写入文本、或数值到 Excel单元格2. 写入数组到 Excel表格

Java并发编程之如何优雅关闭钩子Shutdown Hook

《Java并发编程之如何优雅关闭钩子ShutdownHook》这篇文章主要为大家详细介绍了Java如何实现优雅关闭钩子ShutdownHook,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 目录关闭钩子简介关闭钩子应用场景数据库连接实战演示使用关闭钩子的注意事项开源框架中的关闭钩子机制1.

Maven中引入 springboot 相关依赖的方式(最新推荐)

《Maven中引入springboot相关依赖的方式(最新推荐)》:本文主要介绍Maven中引入springboot相关依赖的方式(最新推荐),本文给大家介绍的非常详细,对大家的学习或工作具有... 目录Maven中引入 springboot 相关依赖的方式1. 不使用版本管理(不推荐)2、使用版本管理(推

Java 中的 @SneakyThrows 注解使用方法(简化异常处理的利与弊)

《Java中的@SneakyThrows注解使用方法(简化异常处理的利与弊)》为了简化异常处理,Lombok提供了一个强大的注解@SneakyThrows,本文将详细介绍@SneakyThro... 目录1. @SneakyThrows 简介 1.1 什么是 Lombok?2. @SneakyThrows

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

Python中的Walrus运算符分析示例详解

《Python中的Walrus运算符分析示例详解》Python中的Walrus运算符(:=)是Python3.8引入的一个新特性,允许在表达式中同时赋值和返回值,它的核心作用是减少重复计算,提升代码简... 目录1. 在循环中避免重复计算2. 在条件判断中同时赋值变量3. 在列表推导式或字典推导式中简化逻辑

判断PyTorch是GPU版还是CPU版的方法小结

《判断PyTorch是GPU版还是CPU版的方法小结》PyTorch作为当前最流行的深度学习框架之一,支持在CPU和GPU(NVIDIACUDA)上运行,所以对于深度学习开发者来说,正确识别PyTor... 目录前言为什么需要区分GPU和CPU版本?性能差异硬件要求如何检查PyTorch版本?方法1:使用命

如何在 Spring Boot 中实现 FreeMarker 模板

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文... 目录什么是 FreeMarker 模板?在 Spring Boot 中实现 FreeMarker 模板1. 环

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll