Java内存溢出排查(必看)

本文围绕Java OOM问题展开,以Scala开发的Web服务为例,介绍了OOM排查及定位方法。通过添加参数生成二进制文件,用工具分析定位问题,找到是因将所有内容存入一个对象致内存占满。还给出解决办法,强调要分析问题根源,MAT工具很实用。

Java OOM毫无疑问是开发人员常见并且及其痛恨的问题,但是任何服务的开发都没法避免OOM。因此,OOM的排查及定位是每个Java工程师都必备的技能。

所遇到的问题

在使用scala开发的一个web服务,在用户使用中,经常出现:java.lang.OutOfMemoryError: Java heap space。而且还束手无策,每次都只能重启服务解决。

准备

服务使用jetty发布的,先来看一下我这个服务的启动参数:

/opt/soft/jdk/jdk1.7.0_40/bin/java \
  -server -Xmx4G -XX:MaxPermSize=1024M -XX:PermSize=256M \
  -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:-CMSConcurrentMTEnabled -XX:CMSInitiatingOccupancyFraction=65 -XX:+CMSParallelRemarkEnabled \
  -XX:+HeapDumpOnOutOfMemoryError \
  -XX:HeapDumpPath=/opt/soft/heapdump/ \
  -Dscala.concurrent.context.numThreads=500 \
  -Dscala.concurrent.context.maxThreads=500 \
  -Dfile.encoding=UTF-8 -jar start.jar >> log 2>&1 &

排查

增加通过了参数-XX:+HeapDumpOnOutOfMemoryError状语从句:-XX:HeapDumpPath当在OOM的时候,会服务在/opt/soft/heapdump下生成一个java_pid$pid.hprof二进制文件。

这个java_pid$pid.hprof也可通过jmap命令来即时生成:jmap -dump:format=b,file=java_pid$pid.hprof $pid($ pid为java进程ID)

下面就是使用工具分析这个.hprof文件来定位问题了。使用Memory Analyzer(MAT)来分析该文件,效果如下:

在这里插入图片描述
效果很吓人,什么鬼,什么东西,吃了3.8G的内存,我#%KaTeX parse error: Double superscript at position 9: #@#@#&^&^̲&#
打开泄漏嫌疑人»泄漏»问题可疑1看到如下详情:
在这里插入图片描述

一开始可能没那么快找到问题,但是这个图已经很明显说明了问题,的英文ArrayList的内容太大,沾满了内存。但是你可能还不清楚具体那块代码导致,时候这个你可以点击那个ArrayList在左侧栏看Attribute
然后一直鼠标右键into进去看里面的详情,最终是可以看内容的。

问题原因
问题排查到最后,ArrayList看到的是里面存的全是ResponseBodyPart,然后就想到了项目使用到Dispatch请求下载结果文件,
于是乎去找到问题代码,错误代码如下:

val outputReq = dispatch.url(url) / "task" / "output" / id
val outputFuture = Http(outputReq > { res =>
  val out = new FileOutputStream(outputFile(taskId), true)
  IOUtils.copy(res.getResponseBodyAsStream(), out)
  out.close
})

看不出问题,感觉一切正常。翻源码会发现,res.getResponseBodyAsStream()之前,将已经所有内容都存入一个ArrayList当中了。哎,没用对啊。

解决办法

问题已经定位到,于是去了解了一下这个项目,该如何使用stream的方式来读取并写入文件流。然后发现,人家有一个逐行读取的实现。但是切割上其实是有问题的,因为拿到一批bytes之后,直接转成了string并用分隔符分割,
奈何内容里面有中文,出现乱码了。

最终,参考项目本身的as.stream.Lines写了一个as.stream.Bytes来通过bytes边读边写,如下:

val bos = new BufferedOutputStream(new FileOutputStream("/tmp/file.txt", true))
val outputFuture = Http(outputReq > as.stream.Bytes(bytes => {
  bos.write(bytes)
}))

总结

主要描述了分析问题的思路和方向,问题都大同小异,OOM总会有原因的,有原因肯定可以找到并解决.MAT这个分析工具很实用,内容很详细。以前遇到OOM问题都是重启服务,治标不治本,还是要多分析问题并解决。

(想自学习编程的小伙伴请搜索圈T社区,更多行业相关资讯更有行业相关免费视频教程等待你来学习。完全免费哦! )

Java内存溢出排查和处理过程如下: #### 确认错误信息 查看系统抛出的错误信息,明确是否为内存溢出错误,以及具体是哪种类型的内存溢出,如堆内存溢出、方法区溢出等。 #### 分析堆栈轨迹 根据错误信息中的堆栈轨迹,定位到可能出现问题的代码位置。堆栈轨迹会显示程序在抛出异常时的调用栈,有助于找出异常发生的源头。 #### 检查代码中的内存问题 找出可能导致内存泄露的代码段,如未关闭的资源(数据库连接、网络连接、文件句柄等),长生命周期对象持有短生命周期对象的引用等。例如: ```java import java.io.FileInputStream; import java.io.IOException; public class ResourceLeakExample { public static void main(String[] args) { try { FileInputStream fis = new FileInputStream("test.txt"); // 处理文件 // 没有关闭文件句柄 } catch (IOException e) { e.printStackTrace(); } } } ``` #### 使用内存分析工具 利用内存分析工具(如JProfiler、MAT、VisualVM等)对Java堆进行深入的分析,找出内存使用的热点。这些工具可以帮助查看对象的创建和销毁情况、对象的引用关系等,从而定位内存泄漏的原因。 #### 生成堆转储文件 当发生内存溢出时,可以生成堆转储文件进行分析,这可以通过`-XX:+HeapDumpOnOutOfMemoryError`和`-XX:HeapDumpPath`参数配置JVM实现。例如,在启动Java程序时添加以下参数: ``` java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/dump.hprof YourMainClass ``` #### 调整JVM参数 根据实际情况调整JVM的堆内存大小、垃圾回收策略等参数。例如,可以通过`-Xmx`和`-Xms`参数分别设置堆的最大和初始大小: ``` java -Xmx2048m -Xms1024m YourMainClass ``` #### 处理内存溢出问题 根据排查结果,对代码进行相应的修改。如关闭未使用的资源、移除不要的引用、优化缓存策略等。例如,使用`try-with-resources`语句确保资源的正确关闭: ```java import java.io.FileInputStream; import java.io.IOException; public class ResourceSafeExample { public static void main(String[] args) { try (FileInputStream fis = new FileInputStream("test.txt")) { // 处理文件 } catch (IOException e) { e.printStackTrace(); } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值