上一篇blog介绍了NMT分析堆外内存分析,但是并未有详细解释为什么我们会遇到JDK8的bug,这篇blog主要来解释清楚,为什么我们会遇到了整个问题。
JDK Bug分析
先对这个JDK Bug做个分析,JDK-8180048 : Interned string and symbol table leak memory during parallel unlinking
注:此bug已经在下面的JDK版本中被修复
问题原因是JDK8 G1回收器并未对已经没有被使用的的intern String进行回收,从而导致堆外内存不断增加,主要就是Symbol域 (参见上一篇关于NMT的介绍);使用CMS不会有这个问题。
可以通过下面2组不同的测试来对比并重现这个问题:
- 测试源码
1 | public class StringInterner { |
- 不使用G1 Collector
1 | java -XX:NativeMemoryTracking=detail -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMTStatistics -XX:MetaspaceSize=100m -XX:MaxMetaspaceSize=100m -XX:+UseStringDeduplication -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -Xloggc:gc-jdk8-marksweep.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xms256m -Xmx256m -cp classes StringInterner |
- 使用G1 Collector重现问题
1 | java -XX:NativeMemoryTracking=detail -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMTStatistics -XX:+UseG1GC -XX:MetaspaceSize=100m -XX:MaxMetaspaceSize=100m -Xloggc:gc-jdk8-g1.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xms256m -Xmx256m -cp classes StringInterner |
检查NMT报告并且搜索Symbol,可以发现使用CMS和G1 Collector的Symbol域内存差异很大,而且G1 Collector的Symbol区域会持续增加。
1 | Symbol (reserved=27549KB, committed=27549KB) |
Jackson JSON 处理 其实到这里已经比较清楚了,为什么Jackson JSON处理会触发JDK这个问题,基本上可以确定的是Jackson一定是在某个地方使用了Intern String导致的。
从Jackson Parser的Feature定义中找到一个选项
1 | /** |