首页 > 代码库 > fastjson class泄漏

fastjson class泄漏

问题描述

fastjson在jetty容器中序列化HttpServletRequest会导致class泄漏,严重时会导致meta区溢出,导致无限FGC。

fastjson序列化流程

fastjson通过动态生成代码提高序列化速度,序列化逻辑如下:

String serialize(Object object) {

    // 先看是否已经存在对应的serializer
    Serializer serializer = serializerCache.get(object.getClass());
    
    if (serializer == null) {
    
        // 通过动态生成代码的方式,创建新的序列器类
        Class<?> serializerClass = createSerializerClass(object.getClass());
        serializer = serializerClass.newInstance();
        serializerCache.put(object.getClass(), serializer);
        
    }
    return serializer.serialize(object);
    
}

如果传入的object类型是org.eclipse.jetty.server.Request,serializerClass.newInstance会出现异常。因此每次序列化org.eclipse.jetty.server.Request,都会生成一个serializerClass。即出现了class泄漏。

PS:  org.eclipse.jetty.server.Request是jetty中HttpServletRequest的实现类,下面简称Request

newInstance()为什么会失败?

fastjson生成的serializerClass结构如下:

import org.eclipse.jetty.server.Request;
 
class Serializer_XXX {

    // Request中每个字段对应一个xxx_asm_field_type字段
    Type context_asm_field_type;
    Type userIdentityScope_asm_field_type;
    // 其他字段省略 ...
 
    // 构造函数
    Serializer_XXX() {
    
        context_asm_field_type = getFieldType( Request.class, "context");
        userIdentityScope_asm_field_type = getFieldType( Request.class, "userIdentityScope");
        // 其他字段忽略 ...
        
    }
}

构造函数会执行失败,原因是找不到org.eclipse.jetty.server.Request。

为什么找不到类?

问题涉及到的3个classloader,它们的结构如下:
ServerClassLoader -> WebAppClassLoader -> AsmClassLoader

Request的classloader是ServerClassLoader,Serializer_XXX的classloader是AsmClassLoader。


按照类加载的约定,加载Request类的流程如下:

技术分享

理论上Request是可以成功加载的。问题出在WebAppClassLoader,它违反了类加载的约定,没有让parent加载,而是自己直接加载,结果找不到,就直接抛ClassNotFoundException。
WebAppClassLoader的行为是可配置的,如果启动参数org.eclipse.jetty.server.webapp.parentLoaderPriority是true,它就会先让parent找,否则就自己找,默认是false

fastjson的问题在哪里?

fastjson实际是有考虑类加载的问题的,它是判断Request的ClassLoader是不是AsmClassLoader或AsmClassLoader的祖先。
但是由于WebAppClassLoader违背了类加载的约定,fastjson的判断就变得不可靠了。


fastjson class泄漏