谁是虚拟内存中的头皮屑?
前言
不知不觉,小y技术人生系列,走过了十期,不知道小y和数据中心的故事,能否在你的人生里有一个美丽的邂逅。连续十期的数据中心故事,大家是不是也有点腻了,今天由小w登场,给大家换换口味,从此也开启小w故事的新篇章之我和中间件的故事O(∩_∩)O~。废话不多说来说我们的故事。

ROUBLE
头皮屑的烦恼
话说在一个莫名其妙的早晨,某行中间业务平台AIX系统虚拟内存使用率过高,触发监控报警。首先AIX工程师出场,定位出为WAS应用服务器占用内存高。是哪些头皮屑占用了内存不释放呢,这次该轮到小w出场了,小w收集了数据进行分析。
NVIRONMENT
生存环境
AIX:6.1
WAS: 8.0.0.6
初始堆1024MB
最大堆3072MB
JRE 1.6.
HO
谁是头皮屑
一、MNON出场


从nmon数据分析,从8点42分左右,java进程(PID:21889322)的CPU以及内存使用率突然增高,随后CPU恢复正常,但是内存一直保持较高状态。CPU最高达到34%,内存最高占用17G。
经判断,该java进程为WAS进程,之后小w从WAS方面排查。
HO
谁是头皮屑
二、javacore出场



大家是否看出了其中的端倪,通过javacore.20151208.112729.21889332.0001.txt查看内存状态,整个JVM占用内存大概12G,其中堆内存占用3G,本地堆内存占用高达9G左右;而其中DirectByteBuffer占用为8G。因此判断主要问题在于DirectByteBuffer占用本地内存偏高,并且使用后长时间没有释放,为什么DirectByteBuffer没有释放呢?
我们先思考下,接下来请出下一个出场人物!
HO
谁是头皮屑
三、SystemOut.log出场



通过SystemOut.log查看,有两个挂起线程有相关DirectByteBuffer堆栈信息,尽管挂起不是该问题的核心内容,但为我们提供了应用程序调用的相关code以及DirectByteBuffer的来源,人物该出场的都出场,让我们来分析下人物的内部关系。
NALYZE
内部关系
从上述的信息,我们可以判断应用执行了类似Download Log的操作,该操作需要向WAS申请Buffer便于输出流操作,由于WAS使用的NIO(异步IO)模式,因此申请Buffer使用的是DirectByteBuffer的模式。

SystemOut.log还没有被我们榨干,我们继续压榨它,从8点37分开始直到8点53分结束,这段期间,一共做了22次Download Log操作。只有这段期间有相关操作,之后以及之前没有相关日志。从时间点上判断,与CPU,内存增长的时间点匹配,可能跟此次虚拟内存高有关,这点已经联系系统管理员确认。但这个属于应用正常的操作,我们重点研究DirectByteBuffer的机制以及为何DirectByteBuffer会占用较高内存,存在DBB泄露。
NALYZE
泄漏!
啊!泄露,凡出现泄露必是不详的预兆,此处也是故事的高潮部分,同时故事的谜底也将揭,一定不要错过哦。
首先我们对DirectByteBuffer(DBB)做下简单介绍:使用本地堆内存,但是他的回收机制是通过对象虚引用,使用java堆的gc方式回收内存。
ISCOVER
现出原形



以上是我们反编译了相关java.nio.Bits.reserveMemory方法实现的DirectByteBuffer申请内存空间的WAS源码,可以看到:DirectByteBuffer的初始内存大小为64M,如果当前申请的内存大于(64M– 已分配内存),那么会进行一次system.gc()的操作,然后根据paramLong(申请空间)以及32M的最大值扩充DBB的内存大小。
DirectByteBuffer就是头皮屑消灭它我们就用system.gc(),但当前的javacore当中看到,WAS的JVM参数当中,存在-Xdisableexplicitgc参数,该参数的意思是禁用任何system.gc(),即对于system.gc()的代码来说,WAS不会执行。因此DirectByteBuffer代码当中的system.gc()也不会执行,造成DBB不会回收空间,也就解释了为什么Download Log之后,CPU下降了,但是内存空间没有明显释放。
NSTRUCTION
官方说明

TORY
后续的故事
当然,对于DirectByteBuffer开启System.gc之外,小w还可以给各位看官针对IBM JDK更多一些的建议:设置-XX:MaxDirectMemorySize=&byte,可以设定DirectByteBuffer的最大值,强迫DBB代码执行System.gc,不会让这个头皮屑无限的扩展,对于咱们的服务器起到了保护作用呀,当然具体的数值还是需要各位高手在测试环境进行相关的测试。
另外,还有一种特别凶狠的招数,那就是禁用WAS的异步IO操作,可以将WebContainer的属性设置为: com.ibm.ws.webcontainer.channelwritetype=sync。
但这种方式,大家还是看看就好,咱们能够简单解决的问题不要搞得太复杂哈。
UCCEED
完美去屑
时间过得好快啊,转眼间我们的故事就要结束了,接下来我们该做些什么呢,就是去屑,我们这可不需要任何名牌洗发水哦,只需要将WAS的JVM参数-Xdisableexplicitgc参数取消,(但与此同时,小w还提醒各位看官,如果去掉此参数,也会导致应用程序的System.gc同时生效呦),到此给我们的故事画上圆满的句号,还给我们虚拟主机一头干净、清爽的的秀发,今天小w的故事就和大家分享完了下期再见喽!






