这星期被线上JVM内存占用不断增大的问题所困扰,自己提出了一些假设,然后去实施验证都一一失败了,有一些经验和教训在这里分享下.

之所以是尴尬,是最后因为修复了另一个看似不相关的问题导致内存不再上升,但这之间的关系还未明了,还需要继续追踪.

这里讲述一下这次排查的过程.


直接内存的错误判断

服务器的JVM配置为Xmx3g,使用G1,没有设置Xms考虑自然收缩和fgc之后的空间回拢.

没有发生过fgc,且堆内存的增长正常,排除掉堆内存的问题.

使用NMT查看各个区域的内存正常,committed内存不足4G,但实际情况内存占用不断增大,甚至出现了OOM killer杀掉java进程.

Total: reserved=5037MB, committed=3926MB
- Java Heap (reserved=3072MB, committed=3072MB)
(mmap: reserved=3072MB, committed=3072MB) - Class (reserved=1196MB, committed=192MB)
(classes #29793)
(malloc=6MB #86203)
(mmap: reserved=1190MB, committed=186MB) - Thread (reserved=186MB, committed=186MB)
(thread #184)
(stack: reserved=184MB, committed=184MB)
(malloc=1MB #922)
(arena=1MB #366) - Code (reserved=273MB, committed=171MB)
(malloc=29MB #39947)
(mmap: reserved=244MB, committed=142MB) - GC (reserved=176MB, committed=176MB)
(malloc=30MB #114115)
(mmap: reserved=146MB, committed=146MB) - Compiler (reserved=1MB, committed=1MB) - Internal (reserved=88MB, committed=88MB)
(malloc=88MB #83467) - Symbol (reserved=31MB, committed=31MB)
(malloc=27MB #312661)
(arena=4MB #1) - Native Memory Tracking (reserved=10MB, committed=10MB)
(tracking overhead=10MB) - Unknown (reserved=6MB, committed=0MB)
(mmap: reserved=6MB, committed=0MB)



内存增长情况(中间下降是进行fgc或重启服务)

最开始考虑是直接内存的问题,观察GC行为发现没有发生过一次fgc,想当然觉得应该是直接内存无法回收,并且在手动执行gc之后内存占用的确是下降的.

于是调整了一波JVM参数,将堆大小设置为2500M,并配置MaxDirectMemory用来触发fgc.

但最后发现内存依旧增长,且还是没有fgc.

再观察了下heap(jmap -heap)的情况,发现手工触发fgc后,heap释放的大小和内存下降几乎一致.

在使用JMX查看了direct pool(java.nio.BufferPool)后惊奇地发现..直接内存并没有多少使用(我们底层用了netty,下意识认为出了问题):



图中所示最大只用了4M左右.

最后设置了Xms之后发现fgc已经没有作用,内存的使用量不会再下降,排除掉了这个因素.

这个错误的判断最主要的原因还是先入为主,没有收集支撑的直接证据.

stack中的间隙

接下去只能使用pmap(pamp -X pid)看下具体的内存信息.

发现了一些有趣的东西,大量的64M块.

直觉告诉我,快找出问题了.

搜索了下发现类似的问题:

解决方法也很直接换一个分配内存的库.

可选的也只有tcmalloc和jemalloc.

选择了前者,配置之后效果出众.

已经没有上图中那么多的64M块了.

并且启动后的内存使用是掉了下来,但是好景不长.



(蓝线是使用了tcmalloc,红线是对比方)

刚开始的确蓝线内存使用好一点(红线在20:00的时候重启 内存降低了).

但随后还是增长了,比较迷的一点是在8点后内存陡降(但JVM没有重启,PID也没有变化),恢复到了最初的水平.

看来问题不在这里.

看似没有关联的问题

周常部署的时候出现一个问题导致CPU飙升,跟了下发现一个处理图片的接口被频繁调用并且在处理较大尺寸的图片,看似和这个堆外内存引起的问题没有关联就没怎么在意.

第二天同事修复了这个问题,迁移走了这个接口到另一个服务.

tcmalloc的heap profiling无法使用,一开启libunwindsegment fault退出,换成jemalloc后开启.

还在想是不是常见的那个zip溢出的问题,但是搜索了代码库没有发现忘记关闭的情况,等着看下增长和dump验证下.

观察了一会儿,突然发现内存不再增长了.



(蓝线是之前一直留着的机器,中间降低是进行了重启)

非常平稳地度过了一天.

那坨profiling也失去了意义... ...

这就感觉非常尴尬了...

继续追踪

更尴尬的是另一个服务炸了... ...

继续跟进,先把jemalloc配置过去,等下一次增长看下dump.

update 2018/09/25



继续观察了一段时间... 发现在内存在突然增大的时候这个部分增加比较多

java2d相关,代码里确实有图片处理.

但放着不管(之前都重启了或者被OOM killer干掉了..)接下去会被回收掉...

update 2018/09/27

之后做了一个实验,读取图片(jpeg),的确是会申请很多native内存.

在爆栈提了个问题:

https://stackoverflow.com/questions/52519253/why-the-code-using-image-io-mallocs-so-much-memory

多线程同时读取内存会升高明显.

大体确定是业务原因,读取处理图片是同步进行,会导致堆外内存占用升高.

fix应该是用任务队列的方式去修改业务实现.

再看看之后会不会有问题.


随着时间的推移遇见了各种各样的问题.

这次排查除了对之前积累的知识的运用,更多像是一次对搜索引擎熟练度的提升

记一次尴尬的Java应用内存泄露排查的更多相关文章

  1. java造成内存泄露原因

    一.Java内存回收机制  不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址.Java中对象是采用new或者反射的方法创建的,这些对象的创建都是在堆(He ...

  2. Java的内存泄露

    Java的内存泄露 1.静态集合类引起内存泄漏 像HashMap.Vector等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,他们所引用的所有的对象Object也不能被释放,因为他们 ...

  3. java中内存泄露有几种?如何分析泄露原因

    一.Java内存回收机制 不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址.Java中对象是采用new或者反射的方法创建的,这些对象的创建都是在堆(Hea ...

  4. java的内存泄露是如何发生的,如何避免和发现

    java的垃圾回收与内存泄露的关系:[新手可忽略不影响继续学习] 马克-to-win:上一节讲了,(i)对象被置成null.(ii)局部对象(无需置成null)当程序运行到右大括号.(iii)匿名对象 ...

  5. Netty堆外内存泄露排查与总结

    导读 Netty 是一个异步事件驱动的网络通信层框架,用于快速开发高可用高性能的服务端网络框架与客户端程序,它极大地简化了 TCP 和 UDP 套接字服务器等网络编程. Netty 底层基于 JDK ...

  6. 记一次Java的内存泄露分析

    当前环境 jdk == 1.8 httpasyncclient == 4.1.3 代码地址 git 地址:https://github.com/jasonGeng88/java-network-pro ...

  7. 记一次Pod中java进程内存“异常”消耗

    背景 环境:openshift3.11 开发反映部署在容器中的java应用内存持续增长,只升不降,具体为: java应用部署在容器中,配置的jvm参数为-Xms1024m -Xmx1024m,容器me ...

  8. (6)java的内存泄露问题

    一:什么是内存泄露--->Java的一个最显著的优势是内存管理.你只需要简单的创建对象而不需要负责释放空间,因为Java的垃圾回收器会负责内存的回收.然而,情况并不是这样简单,内存泄露还是经常会 ...

  9. Diagnosing out of memory errors and memory leaks 内存泄露实例 C Java JavaScript 内存泄露

    小结: 1. 数据库连接池. JDBC语句和结果对象必须显式地关闭. 2. 电梯到目标楼层后地址是否被释放 When a button is pressed: Get some memory, whi ...

随机推荐

  1. vue学习之响应式原理的demo实现

    Vue.js 核心: 1.响应式的数据绑定系统 2.组件系统. 访问器属性 访问器属性是对象中的一种特殊属性,它不能直接在对象中设置,而必须通过 defineProperty() 方法单独定义. va ...

  2. Eclipse 4.2 安装Java反编译插件

    在eclipse下安装反编译插件可以直接查看 .class 文件对应的java源码. 反编译插件有 jdeclipse 和 jadeclipse. (1) jdeclipse http://www.d ...

  3. 吐槽一下--最近多次在腾讯以及万科的面试经历---Web前端与PHP后端开发

    前端时间,由于职业发展等,想要换一份工作,于是投递了一些国内还算知名的公司,列如: 腾讯.万科之类的: (1)首先说一下这两家公司的反馈情况: 腾讯:投递到反馈,(初次人事打电话沟通)大约1周,三次不 ...

  4. fiddler抓包软件的使用--请求头--ajax

    User-Agent: FiddlerHost: localhost:49828Content-Length: 0Accept: application/xmlContent-Type: applic ...

  5. BZOJ_1015_[JSOI2008]星球大战_并查集

    BZOJ_1015_[JSOI2008]星球大战_并查集 题意:很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系.某一天,凭着一个偶然的 机遇,一支反抗军摧毁了帝国的超级武器, ...

  6. GraphQL 入门介绍

    写在前面 GraphQL是一种新的API标准,它提供了一种更高效.强大和灵活的数据提供方式.它是由Facebook开发和开源,目前由来自世界各地的大公司和个人维护.GraphQL本质上是一种基于api ...

  7. typecho设置文章密码保护

    在别人博客看到了一个需要输入密码才能访问文章的功能,像下图一样 typecho也是有这个功能,不需要插件就可以实现.在编辑文章时,右边高级选项,公开度里有个密码保护可以选择 效果图 不过这样的界面不是 ...

  8. 【.NET异步编程系列2】掌控SynchronizationContext避免deadlock

    引言: 多线程编程/异步编程非常复杂,有很多概念和工具需要去学习,贴心的.NET提供Task线程包装类和await/async异步编程语法糖简化了异步编程方式. 相信很多开发者都看到如下异步编程实践原 ...

  9. 支持向量机(Support Vector Machine,SVM)—— 线性SVM

      支持向量机(Support Vector Machine,简称 SVM)于 1995 年正式发表,由于其在文本分类任务中的卓越性能,很快就成为机器学习的主流技术.尽管现在 Deep Learnin ...

  10. python接口自动化(十五)--参数关联接口(详解)

    简介 我们用自动化新建任务之后,要想接着对这个新建任务操作,那就需要用参数关联了,新建任务之后会有一个任务的Jenkins-Crumb,获取到这个Jenkins-Crumb,就可以通过传这个任务Jen ...