1、问题的产生

线上监控出现A(这里的别名)告警,如图:

java 去永久代 jvm永久代满了怎么办_字符串

2、问题排查 

2.1 看下堆栈情况

jmap -heap 进程号

可以很直观看出永久代一直处于很高。

java 去永久代 jvm永久代满了怎么办_java_02

2.2 看下该服务相应的进程CPU情况

top -H -p  进程号

可以看出并没有因为哪个线程导致可能性的问题。

java 去永久代 jvm永久代满了怎么办_java_03

2.3 查看下gc的情况

jstat -gc 进程号 时间(毫秒)

从 gc 来看,并没有触发full gc ,但是也可以很直观看出,永久代一直是沾满的。

java 去永久代 jvm永久代满了怎么办_java 去永久代_04

2.4 开始下载dump文件 

方式一:对应用新增JVM启动参数

-XX:+HeapDumpOnOutOfMemoryError

-XX:HeapDumpPath=${目录}。

方式二:直接执行命令(强烈不推荐,尤其是非分布式环境下

jmap -dump:format=b,file=serviceDump.dat 进程号   (注意:该方式在执行时,JVM是暂停服务的,也就是说整个java进程是停止了,所以对线上的运行会产生影响)

从永久代溢出的可能性:

①永久代溢出——方法区溢出 
方法区存放Class的相关信息,下面借助CGLib直接操作字节码,生成大量的动态类。

②永久代溢出——常量池溢出 
要模拟常量池溢出,可以使用String对象的intern()方法。如果常量池包含一个此String对象的字符串,就返回代表这个字符串的String对象,否则将String对象包含的字符串添加到常量池中。

楼主已经生成了相关的dump文件,是以dat结尾(serviceDump.dat)。然后用到JDK自带的工具 jvisualvm.exe

java 去永久代 jvm永久代满了怎么办_字符串_05

java 去永久代 jvm永久代满了怎么办_jvm_06

至此,我们发现产生了大量的序列化的ASMSerializer_1_ChannelMerchInfo。

重点来了!!!重点来了!!!重点来了!!!

然后马上去翻最新一次上线的代码,如图:

java 去永久代 jvm永久代满了怎么办_java 去永久代_07

java 去永久代 jvm永久代满了怎么办_java 去永久代_08

因为打印日志那块是核心业务,所以被执行次数会很多,因此不断地调用JsonUtil,意味着每次都new SerializeConfig()。接下来进去看其源码,首先是 new SerializeConfig(),一路跟下去,就会发现这里用了ASM动态代理。

 

java 去永久代 jvm永久代满了怎么办_java 去永久代_09

然后继续跟 JSON.toJSONString(),再玩下走,就会发现 com.alibaba.fastjson.serializer.ASMSerializer_1_ChannelMerchInfo,和JVM分析的Dump文件内容匹配上了。

java 去永久代 jvm永久代满了怎么办_java_10

java 去永久代 jvm永久代满了怎么办_jvm_11

最后得出结论,系统每次都调用JSON.toJSONString的时候,都把config(即:new SerializeConfig())传进去,意味着每次都是用新的ASM代理工厂序列化新的ChannelMerchInfo代理类,所以就造成本次的问题。正常的应该是静态的ASM代理工厂,然后每次地序列化同一个ChannelMerchInfo代理类,就不会产生新的了。

3、解决办法

将new SerializeConfig()从问题方法体抽出,然后将其静态化,最后问题得以解决。

 

java 去永久代 jvm永久代满了怎么办_jvm_12