『jvm』定位项目每天上午9点半 Full GC 问题

2020-10-30  本文已影响0人  Yellowtail

[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

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.DelegatingClassLoadercom.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

error

执行的时候,抛异常了,不过没关系,部分类已经下载下来了


class

第三步, 反编译查看

javap -v GeneratedMethodAccessor753.class

随便看一个


javap

可以看到是我们的一个模型

再看一个,依旧是我们的模型


city

debug 确定

本地debug前,新增jvm参数

-XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+TraceClassLoading -XX:+TraceClassUnloading

执行的时候可以看到


image.png

同时,可以看到 jackson 序列化的时候,也会生成

jackson

到了这里,有两个嫌疑犯 dozerjackson

jackson

我们的所有项目都用到了jackson,用在接口入参反序列化到java对象和 java对象序列化为 response body

去其它项目看下,就知道 jackson 运行一段时间后的表现

选了一个用了 jackson 但是没有 dozer 的项目

image.png

经过对比,没有得出什么结论,因为这个项目 vo对象少
所以jackson 依旧属于嫌疑犯

初步结论

jackson

java对象序列化为二进制的 http body 业界常用工具
属于非用不可的,后续可以思考一下如何优化

dozer

java对象间互相转换的工具,已被业界弃用,很古老的工具
可以不用

参考

metadata
StringTable
YoungGC
假笨说-从一起GC血案谈到反射原理

上一篇 下一篇

猜你喜欢

热点阅读