Springboot Actual e-commerce project Mall4j (https://gitee.com/gz-yami/mall4j)

Java open source mall system

Environment: jdk1.8

System: Windows/Linux

Fastjson version: 1.2.29

Key code:

public class FastJsonUtil {
    /* * Convert the POJO object to a JSON string, and change the camel name to an underscore name */
    public static String buildData(Object bean) {
        try {
            SerializeConfig config = new SerializeConfig();
            config.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
            return JSON.toJSONString(bean, config);
        } catch (Exception e) {
            return null; }}}Copy the code
// An arbitrary entity class
public class T {
    public String userName;
    public String userArg;
    public String userGender;
    public String userAddr;

    public T(String userName, String userArg, String userGender, String userAddr) {
        this.userName = userName;
        this.userArg = userArg;
        this.userGender = userGender;
        this.userAddr = userAddr; }}Copy the code

Simulate concurrency (you can also use JMeter)

public class Main {
    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(6.6.1, TimeUnit.SECONDS, new ArrayBlockingQueue<>(999999));
        for (;;) {
            // Submit tasks every 1 millisecond
            TimeUnit.MILLISECONDS.sleep(1); poolExecutor.submit(Main::prcess); }}// Simulate a request call that returns a T object in JSON format
    public static void prcess(a) {
        T t = new T("1"."2"."3"."4");
        String res = FastJsonUtil.buildData(t);
        // System.out.printf(res);}}Copy the code

JVM startup parameters:

-xMS4g -XMx4g -xmn2g -xSS1024K -xx :+UseConcMarkSweepGC (limit heap memory size, adjust ratio of new age area to old age to 1:1, set thread stack size, and use CMS garbage collector)

After running for some time, you will find that the Java program takes up much more memory than 4g.

Since the heap size has been set to 4g and the application is using much more than 4G and climbing directly into physical memory, it is possible that out-of-heap memory (direct memory or metaspace memory) is leaking.

In this program code, there is no direct memory operation, no use of NETty and IO related framework.

Add a startup parameter -xx :MaxDirectMemorySize=1g, restart the project, find no effect, the problem still exists.

So lock the problem down to Matespace and use jmap-heap PID to check heap usage:

You can see that MaxMetaspaceSize is the system memory size, and there is no limit. So it could be that after a fastjson method is called, it processes something that needs to be stored in Metaspace, and since Metaspace has no limit on memory size, the Java application’s memory usage is climbing to more than 4 gigabytes, eventually triggering a memory alert.

-xx :MetaspaceSize=256m -xx :MaxMetaspaceSize=512m No limit value has been exceeded.

So, at this point, you can determine that the metaspace memory footprint is too large because fastjson may be loading a class all the time.

Start the project again with -verbose:class to see which class is reloaded

[Loaded com.alibaba.fastjson.serializer.ASMSerializer_1_T from File: E: / maven/repository/com/alibaba/fastjson / 1.2.29 / fastjson - 1.2.29. Jar] [the Loaded com.alibaba.fastjson.serializer.ASMSerializer_1_T from File: E: / maven/repository/com/alibaba/fastjson / 1.2.29 / fastjson - 1.2.29. Jar] [the Loaded com.alibaba.fastjson.serializer.ASMSerializer_1_T from File: E: / maven/repository/com/alibaba/fastjson / 1.2.29 / fastjson - 1.2.29. Jar] [the Loaded com.alibaba.fastjson.serializer.ASMSerializer_1_T from File: E: / maven/repository/com/alibaba/fastjson / 1.2.29 / fastjson - 1.2.29. Jar]...Copy the code

In summary, it is obvious that the class that has been loading is ASMSerializer_1_T.

The next step is to find out where Fastjson keeps reloading the class.

Starting with the json.tojsonString method, Into the com. Alibaba. Fastjson. JSON# toJSONString (java.lang.object, com. Alibaba. Fastjson. The serializer. SerializeConfig, com.alibaba.fastjson.serializer.SerializeFilter[], java.lang.String, int, com.alibaba.fastjson.serializer.SerializerFeature…) In the method

GetObjectWriter (clazz) -> config.getObjectWriter(clazz) -> put(clazz, CreateJavaBeanSerializer (clazz)) -> createJavaBeanSerializer(beanInfo) -> createASMSerializer(beanInfo) Method

In createASMSerializer, there are several lines of code

.// Concatenate the class name
String className = "ASMSerializer_" + seed.incrementAndGet() + "_" + clazz.getSimpleName();
String packageName = ASMSerializerFactory.class.getPackage().getName();
String classNameType = packageName.replace('. '.'/') + "/" + className;
String classNameFull = packageName + "." + className;
ClassWriter cw = new ClassWriter();
// Then the ASMSerializer_ class is loaded
cw.visit(V1_5 //
         , ACC_PUBLIC + ACC_SUPER //
         , classNameType //
         , JavaBeanSerializer //
         , new String[] { ObjectSerializer } //); .Copy the code

So, here it is, and every time it calls here, it loads ASMSerializer_1_T into metaspace.

This code is in ASMSerializerFactory.

Back in the user code, on the line SerializeConfig Config = new SerializeConfig(), go to the no-argument construct of SerializeConfig, and its parameterized construct is called. There are several lines of code in the parameter construct

if (asm) {
    asmFactory = new ASMSerializerFactory();
}
Copy the code

It’s clear that ASMSerializerFactory will be created repeatedly because SerializeConfig is always created, ASMSerializerFactory then call createASMSerializer methods in this class, would lead to repeated loading com. Alibaba. Fastjson. Serializer. ASMSerializer_1_T

Solutions:

Set SerializeConfig Config = new SerializeConfig(); config.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase; These two lines of code are outside the method, modified as follows:

public class FastJsonUtil {
     private final static SerializeConfig config = new SerializeConfig();
     static {
         config.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
     }
    /* * Convert the POJO object to a JSON string, and change the camel name to an underscore name */
    public static String buildData(Object bean) {
        try {
            return JSON.toJSONString(bean, config);
        } catch (Exception e) {
            return null; }}}Copy the code

Restart the project, the problem is resolved, and the ASMSerializer_1_T class is no longer loaded all the time.

Use JVisualVm to observe memory activity

Metaspace before modification:

Modified Metaspace:

You can obviously see that class and Metaspace memory are no longer loaded as frequently as they should be.

Springboot Actual e-commerce project Mall4j (https://gitee.com/gz-yami/mall4j)

Java open source mall system