最近公司测试环境Cassandra的一次PermGen Space,好久没排查此类问题了,对JVM也有点陌生,借此机会重新学习学习,经过一段时间的不断尝试,终于基本定位问题所在,PermGen OOM 是因为Casandra 在运行过程中不断的往PermGen里面放入String常量导致OOM,但没找到Cassandra在什么情况下会不断的产生新的String 常量,还要继续跟进。
排查过程如下:
好久没真正排查OOM问题了,一开始也没什么办法,只好重新启动Cassandra ,过一段时间又继续OOM,一直感觉很诡异,之前运行那么久都没有问题,怎么现在老是OOM,查看OOM日志,也没什么明确信息,然后拿着OOM时的java_pidXXX.hprof文件,通过Eclipse 的MemoryAanlyzerTool工具分析,看了半天,也没看出来什么明显问题,后来无意中发现jmap这个命令(其实之前也用过,好久没用了都忘得差不多了,看来久不久得练练)可以直接查看当前heap使用情况,直接jmap -heap pid,发现我们Cassandra实例内存默认配置如下:
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 2147483648 (2048.0MB)
NewSize = 419430400 (400.0MB)
MaxNewSize = 419430400 (400.0MB)
OldSize = 5439488 (5.1875MB)
NewRatio = 2
SurvivorRatio = 8
PermSize = 21757952 (20.75MB)
MaxPermSize = 85983232 (82.0MB)
PernGen Size 的值使用JVM默认值,配置中没有具体指定,而我们的Cassandra OOM以后又启动起来,Perm Generation 已经使用了81M,接近100%的使用率,怪不得运行一段时间以后又继续OOM,只好手动设置Cassandra MaxPermSize=256M,重新启动,感觉这样也没查出具体问题,继续查看permstat,命令
jmap -permstat pid
发现218566 intern Strings occupying 66681200 bytes,终于确定问题所在,原来里面保存了65M的String常量
现在基本确定OOM的原因了,但还是没搞清楚Cassandra是如何在运行过程中把String常量放入PermGen,还需继续努力。
下面是测试String的不同使用方法对JVM内存的使用情况:
环境:
java version "1.6.0_51"
jvm: Java HotSpot(TM) 64-Bit Server VM (build 20.51-b01-457, mixed mode)
程序伪代码1:
i=0
if(i<100000)
i++;
String.valueOf(i).intern()
结论:
1、String 对象会在新生代创建并占用内存
2、String 的值会被放入永久代中并占用内存
2、当新生代GC时,不会把String对象指向的String字符串转入老年代中,这些字符串已经被放入永久代,只会把String对象其他信息(具体还不能确定是什么信息)放入老年代
占用内存相对较少,但String字符串存放在永久代,下面是YGC=4的时候,JVM使用情况
S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT
2112.0 2112.0 9.8 0.0 17024.0 571.0 63872.0 7337.4 11264.0 11120.7 4 0.023 0 0.000 0.023
程序伪代码2:
i=0
if(i<100000)
i++;
String.valueOf(i) 或者new String(i+"")或者 i+""
结论:
1、String 对象会在新生代创建并占用内存
2、String 的值不会被放入永久代中并占用内存
2、当新生代GC时,会把String对象指向的String字符串和String对象其他信息都放入老年代
new String(i+"") 占用内存相对较少,下面是YGC=4的时候,JVM使用情况
S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT
2112.0 2112.0 2112.0 0.0 17024.0 5835.4 63872.0 28361.7 5120.0 4952.8 4 0.064 0 0.000 0.064
i+"“ 与 new String(i+"") 占用内存差不多,下面是YGC=4的时候,JVM使用情况
S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT
2112.0 2112.0 2112.0 0.0 17024.0 965.2 63872.0 28373.7 5120.0 4952.5 4 0.081 0 0.000 0.081
String.valueOf(i) 占用内存相对较多,下面是YGC=4的时候,JVM使用情况
S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT
2112.0 2112.0 2112.0 0.0 17024.0 571.0 92956.0 55772.8 8252.0 4949.8 4 0.129 96 0.468 0.598