[TOC]
背景
每天上午9点半左右,生产环境上我们的项目会发生 Full GC
我这篇博客的思路和步骤基本是根据大佬的文档 假笨说-从一起GC血案谈到反射原理 来的,按图索骥,也把大佬的一些步骤没有写清楚的地方写清楚了
GC 日志
提前配置好 jvm
参数
-Xms2048m -Xmx2048m -Xmn830m -XX:SurvivorRatio=8 -XX:-UseAdaptiveSizePolicy -Duser.timezone=GMT+08 -XX:-OmitStackTraceInFastThrow -XX:+PrintCommandLineFlags -XX:MetaspaceSize=400m -Xloggc:/ms/jvm/gc/c.log -XX:+PrintHeapAtGC -XX:+PrintReferenceGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/ms/jvm/oom/ -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
metadata space
可以看到 capacity
,这个是已经使用的内存大小(具体参数可以参考 metaspace )
可以发现,这个空间一直在增长
每天达到 9:30
左右的时候,就会触发 Full GC
metaspace
那么这个 metaspace
占用的都是谁呢?
通过 arthas
可以看下
下载 arthas
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar --repo-mirror aliyun --use-http
查看 class loader
class loader依次分析一下
org.apache.catalina.loader.ParallelWebappClassLoader
tomcat 的类加载器,一个实例,加载了 23935 个类
sun.reflect.DelegatingClassLoader
反射,10666
个实例,每个实例只加载一个
BootstrapClassLoader
称为启动类加载器,是Java类加载层次中最顶层的类加载器,负责加载JDK中的核心类库,如:rt.jar、resources.jar、charsets.jar等
一个实例,加载了8391个类
com.navercorp.pinpoint.bootstrap.classloader.ParallelCapablePinpointURLClassLoader
arms,一个实例
com.taobao.arthas.agent.ArthasClassloader
arthas 自己
java.net.URLClassLoader
网络请求,一个实例
com.navercorp.pinpoint.common.plugin.PluginLoaderClassLoader
arms
com.alibaba.fastjson.util.ASMClassLoader
fastjson, 3个实例,加载了88
后面都是毛毛雨,影响不大,不分析了
初步判定
实例数量大于1的有 sun.reflect.DelegatingClassLoader
和 com.alibaba.fastjson.util.ASMClassLoader
其中 fastjson
虽然有问题,但是问题不大,暂时不care
那么问题就出在 sun.reflect.DelegatingClassLoader
预测是 dozer
导致的 (我们项目中用到反射的东西不多)
揪出元凶
根据大佬的文档 假笨说-从一起GC血案谈到反射原理
第一步,写一个类
import sun.jvm.hotspot.tools.jcore.ClassFilter;
import sun.jvm.hotspot.oops.InstanceKlass;
public class MethodAccessorFilter implements ClassFilter {
@Override
public boolean canInclude(InstanceKlass kls) {
String klassName = kls.getName().asString();
return klassName.startsWith("sun/reflect/GeneratedMethodAccessor");
}
}
第二步, 导出
java -classpath ".:$JAVA_HOME/lib/sa-jdi.jar" -Dsun.jvm.hotspot.tools.jcore.filter=MethodAccessorFilter -Dsun.jvm.hotspot.tools.jcore.outputDir=/usr/local/tomcat/jvmlogs/code sun.jvm.hotspot.tools.jcore.ClassDump 1
导出目录为: /usr/local/tomcat/jvmlogs/code
执行的时候,抛异常了,不过没关系,部分类已经下载下来了
class
第三步, 反编译查看
javap -v GeneratedMethodAccessor753.class
随便看一个
javap
可以看到是我们的一个模型
再看一个,依旧是我们的模型
city
debug 确定
本地debug前,新增jvm
参数
-XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+TraceClassLoading -XX:+TraceClassUnloading
执行的时候可以看到
image.png
同时,可以看到 jackson
序列化的时候,也会生成
到了这里,有两个嫌疑犯 dozer
和 jackson
jackson
我们的所有项目都用到了jackson
,用在接口入参反序列化到java对象和 java对象序列化为 response body
去其它项目看下,就知道 jackson
运行一段时间后的表现
选了一个用了 jackson
但是没有 dozer
的项目
经过对比,没有得出什么结论,因为这个项目 vo对象少
所以jackson
依旧属于嫌疑犯
初步结论
jackson
是java对象
序列化为二进制的 http body 业界常用工具
属于非用不可
的,后续可以思考一下如何优化
dozer
是java对象
间互相转换的工具,已被业界弃用,很古老的工具
可以不用
网友评论