Java字符串及常量池
Contents
基于Mac 和 JDK 1.8
统计 String 对象相关信息
# 导出当前内存快照
jmap -dump:format=b,file=/tmp/java-app-${PID}.hprof ${PID}
然后用 MAT 打开分析
/Applications/mat.app/Contents/MacOS/MemoryAnalyzer -vm /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/bin/java -vmargs -Xmx8g -XX:-UseGCOverheadLimit
然后打开 Histogram
再按如下操作, 选 Group by Value
然后得到类似如下的结果:
想要查看哪些对象持有它, 可以在某个String 对象上, 右键选择
关于 incomming 和 outgoing , 可以这样子理解:
Incomming -> 生成(持有)该对象 --> outgoing
intern 方法
它的方法签名为
public native String intern();
可知, 它是一个 native 方法. 调用 native 方法是很耗时的.
它的作用是, 池化该字符串对象. (即相同的字符串, 在内存中, 只有一份占用空间)
@Test
public void testIntern() throws InterruptedException {
String a = "hello";
String b = new String("hello");
System.out.println(a == b);
String c = b.intern();
System.out.println(a == c);
}
上面的代码在 Java 8 中输出为 false true
什么时候才考虑用 intern 呢? 长期存活的对象
并且有 大量重复的字符串
并且不太注重性能
, 这样子就可以节省大量的内存空间. 但要注意该方法的性能是比较低的
与 disruptor 相关
线上的 DSP 广告系统, 通过 disruptor 来进行竞价日志队列处理. 由于没添加相关的清除对象的 handler , 导致整个 JVM 周期中持有大量的 String 对象(如上图). 这点要特别注意.
它导致 JVM 的 old gen 在增长, 然后触发 full gc 的频率大.
我的解决方案是, 添加了个 clearHandler , 在该 handler 中清除对象. 可参考另一篇文章 /2019/10/12/disruptor学习/#清除对象-1