Java 内存溢出排查

标签: 基础技术 内存管理 排错 | 发表时间:2018-09-17 09:55 | 作者:TiuVe2
出处:http://www.importnew.com

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 的时候,服务会生成一个  java_pid$pid.hprof 二进制文件。

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

效果很吓人,什么鬼,什么东西,吃了 3.8G 的内存,我#%$#@#@#&^&^&#$….
打开 Leak Suspects» Leaks» Problem Suspect 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 的方式来读取并写入文件流。然后发现,人家有一个  read line by line 的实现。但是切割上其实是有问题的,因为拿到一批 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 问题都是重启服务,治标不治本,还是要多分析问题并解决。

相关文章

相关 [java 内存 溢出] 推荐:

Java 内存溢出排查

- - ImportNew
Java OOM 毫无疑问是开发人员常见并且及其痛恨的问题,但是任何服务的开发都没法避免 OOM. 因此,OOM 的排查及定位是每个 Java 工程师都必备的技能. 在使用 scala 开发的一个 web 服务,在用户使用中,经常出现:  java.lang.OutOfMemoryError: Java heap space .

Java内存溢出与栈溢出

- - CSDN博客推荐文章
JVM运行时内存 = 共享内存区 + 线程内存区. 共享内存区 = 持久带 + 堆. 持久带 = 方法区 + 其他. 堆 = Old Space + Young Space. JVM用持久带(Permanent Space)实现方法区,主要存放所有已加载的类信息,方法信息,常量池等等. 可通过-XX:PermSize和-XX:MaxPermSize来指定持久带初始化值和最大值.

Java 内存区域与内存溢出

- - CSDN博客综合推荐文章
Java虚拟机在执行Java程序的过程中会把他所管理的内存划分为若干个不同的数据区域. Java虚拟机规范将JVM所管理的内存分为以下几个运行时数据区:程序计数器,Java虚拟机栈,本地方法栈,Java堆,方法区. 下面详细阐述各数据区所存储的数据类型. 程序计数器(Program Counter Register).

Java 堆内存溢出梗概分析

- - ITeye资讯频道
原文:Java Out of Memory Heap Analysis. 链接: https://dzone.com/articles/java-out-of-memory-heap-analysis. 译者:dreamanzhao, 无若. 任何使用过基于 Java 的企业级后端应用的软件开发者都会遇到过这种低劣、奇怪的报错,这些报错来自于用户或是测试工程师: java.lang.OutOfMemoryError:Java heap space.

java 内存溢出 栈溢出的原因与排查方法

- - 互联网 - ITeye博客
 1、 内存溢出的原因是什么.       内存溢出是由于没被引用的对象(垃圾)过多造成JVM没有及时回收,造成的内存溢出. 如果出现这种现象可行代码排查:.     一)是否App中的类中和引用变量过多使用了Static修饰 如public staitc Student s;在类中的属性中使用 static修饰的最好只用基本类型或字符串.

java内存泄露和内存溢出区别

- - 互联网 - ITeye博客
虽然jvm可以通过GC自动回收无用的内存,但是代码不好的话仍然存在内存溢出的风险. 最近在网上搜集了一些资料,现整理如下:. 一、为什么要了解内存泄露和内存溢出. 1、内存泄露一般是代码设计存在缺陷导致的,通过了解内存泄露的场景,可以避免不必要的内存溢出和提高自己的代码编写水平;. 2、通过了解内存溢出的几种常见情况,可以在出现内存溢出的时候快速的定位问题的位置,缩短解决故障的时间.

Java内存溢出问题的定位过程

- - Java - 编程语言 - ITeye博客
相信通过写java程序讨生活的人对内存溢出并不陌生,如下文字的出现更是让人恼火:. 尤其当应用服务器(Java容器)出现上述情况更是让人有一种天塌下来的感觉.    好的编码实践可能会大大降低内存溢出的产生.    本文并不是写如何规避内存溢出,但是我还是要介绍一下如何能够尽量规避内存溢出:. 找几个资深程序猿(或者整个项目组讨论后)写一个Java编码规范,让项目组成员尽量遵守.

译文:解密Java内存溢出之持久代

- - 研发管理 - ITeye博客
       垃圾回收是Java程序员了解最少的一部分. 他们认为Java虚拟机接管了垃圾回收,因此没必要去担心内存的申请,分配等问题. 但是随着应用越来越复杂,垃圾回收也越来越复杂,一旦垃圾回收变的复杂,应用的性能将会大打折扣. 所以,Java程序员了解垃圾回收的机制并且知道怎样解决“内存溢出”问题会有很大的益处.

Java常见问题分析(内存溢出、内存泄露、线程阻塞等)

- - Java - 编程语言 - ITeye博客
Java垃圾回收机制(GC) . 堆内存3代分布(年轻代、老年代、持久代) . ML(内存泄露) OOM(内存溢出)问题现象及分析 . IBM DUMP分析工具使用介绍. Java应用CPU、线程问题分析. Java垃圾回收机制(GC). 1.GC机制作用 . 1.1 JVM自动检测和释放不再使用的对象内存 .

使用JProfiler解决Java应用程序内存溢出问题实例

- - Java - 编程语言 - ITeye博客
    前段时间基于OpenJms部署了一个消息中间件服务器,通过主题订阅模式在各个消息节点之间传递信息,但是某个类型的消息节点长时间运行后出现了内存溢出问题,最后使用JProfiler的基本线程监测功能找到问题所在,并且进行解决. Java 版本 java version "1.7.0_40". JProfiler 版本 v8.0.7.