Tomcat applications from the parallel stream class loading problems

This article first appeared in vivo micro-channel public number of Internet technology 
links: https://mp.weixin.qq.com/s/f-X3n9cvDyU5f5NYH6mhxQ
Author: Xiao Ming Xuan, Wang ring

With the growth in popularity of Java8, more and more developers use parallel streams (parallel) this feature to enhance the efficiency of code execution. However, the authors found that in the case of a Tomcat container dynamically loaded class failed parallel streams will appear by comparing multiple versions of Tomcat source code, combined with the principle of parallel streams and JVM class loading mechanism successfully locate the source of the problem. This paper to analyze this question, and give solutions.

First, the problem scene

In certain applications, it will flow through the parallel Dubbo call when the service starts, the calling code as follows:

Lists.partition(ids, BATCH_QUERY_LIMIT).stream()
     .parallel()
     .map(Req::new)
     .map(client::batchQuery)
     .collect(Collectors.toList());
Call logs found a large number of WARN log com.alibaba.com.caucho.hessian.io.SerializerFactory.getDeserializer Hessian / Burlap: 'XXXXXXX' is an unknown class in null: java.lang.ClassNotFoundException: XXXXXXX, the interface returns the result in the use when an error is thrown java.lang.ClassCastException: java.util.HashMap can not be cast to XXXXXXX.

Second, Analysis

1, the initial positioning

First, according to the error log it can be seen that, since the return parameter dependent Dubbo service entity class is not found, resulting in Dubbo not return data message into corresponding entity deserialization, forced conversions reported type java.lang. ClassCastException. Through the thread stack and WARN log positioning to the class of problems is com.alibaba.com.caucho.hessian.io.SerializerFactory , due  _loader  it is not possible to load the class is null, the relevant code is as follows:

try {
       Class cl = Class.forName(type, false, _loader);
       deserializer = getDeserializer(cl);
   } catch (Exception e) {
       log.warning("Hessian/Burlap: '" + type + "' is an unknown class in " + _loader + ":\n" + e);
    log.log(Level.FINER, e.toString(), e);
   }

 Next, how to locate the upwardly ** _loader ** to be  null , SerializerFactory  construction method for  _loader  initialized, the initialization code as can be seen  _loader  used are contextClassLoader current thread.

public SerializerFactory() {
    this(Thread.currentThread().getContextClassLoader());
}
 
public SerializerFactory(ClassLoader loader) {
    _loader = loader;
}

 According to see the stack of the current thread ForkJoinWorkerThread, ForkJoinWorkerThread work threads within Fork / Join frame (Java8 parallel streams is used Fork / Join). JDK documentation states:

The context ClassLoader is provided by the creator of the thread for use by code running in this thread when loading classes and resources. If not set, the default is the ClassLoader context of the parent Thread.

Therefore, the current thread contextClassLoader should create this thread and parent thread keeping fishes should not be null ah?

Continue to look at the source code ForkJoinWorkerThread created, the first to use ForkJoinWorkerThreadFactory create a thread, and then create a thread to ForkJoinPool registration, the thread initialization logic and common threads and no difference was found alone problem of JDK itself difficult to find, so the analysis will be transferred to Tomcat in.

2, Tomcat upgrade problems caused by

Some versions take Tomcat7.0.x to do the experiment and compared and found no such problems versions prior to 7.0.74, but later versions 7.0.74 there have been similar problems, results in the following table.

So far the problem has been targeting the Tomcat version is caused by the source code comparison and found Tomcat 7.0.74 release after more than a code like this:

if (forkJoinCommonPoolProtection && IS_JAVA_8_OR_LATER) {
    // Don't override any explicitly set property
    if (System.getProperty(FORK_JOIN_POOL_THREAD_FACTORY_PROPERTY) == null) {
        System.setProperty(FORK_JOIN_POOL_THREAD_FACTORY_PROPERTY,
                "org.apache.catalina.startup.SafeForkJoinWorkerThreadFactory");
    }
}

 

private static class SafeForkJoinWorkerThread extends ForkJoinWorkerThread {
 
   protected SafeForkJoinWorkerThread(ForkJoinPool pool) {
       super(pool);
       setContextClassLoader(ForkJoinPool.class.getClassLoader());
   }
}

In Java8 environment, Tomcat version 7.0.74 by default after the  SafeForkJoinWorkerThreadFactory  as ForkJoinWorkerThread creation of a factory, while contextClassLoader set the thread's ForkJoinPool.class.getClassLoader (), ForkJoinPool rt.jar package is part of a class by BootStrap ClassLoader loading, the corresponding class loader to null. So far, _loader empty for the issue has been clear, but why bother Tomcat, null as contextClassLoader this ForkJoinWorkerThread it?

Continue contrast changeLog Tomcat's  http://tomcat.apache.org/tomcat-7.0-doc/changelog.html found in Tomcat This release fixes a memory leak triggered by the ForkJoinPool Bug 60620 - [JRE] Memory leak found in java. util.concurrent.ForkJoinPool, why the thread contextClassLoader cause a memory leak it?

3, contextClassLoader memory leaks Mystery

After JDK1.2, parent class loader delegation model has been widely introduced. It's working process is: if a class loader loads the class received a request, it first does not own to try to load this class, but to delegate the entire request to the parent class loader to complete each level of the class loader this is true, all load request should be transferred eventually to the top of the boot class loader, only when the parent loader to load the feedback they can not complete the request, the child loader will try to load their own, following the flow chart.

However, parents delegated model does not guarantee that the process of application loads the class, a typical example is the JNDI service, these interface definitions provided by a third party to achieve in rt.jar, Bootstrap ClassLoader obviously do not know the code. To solve this problem, JDK1.2 while introducing the thread context class loader (Thread Context ClassLoader) were loaded class, as parents complementary model of delegation.

Memory leak issue back on, imagine a scenario, if a thread holds the ClassLoaderA (by the ClassLoaderA loaded several classes), when an application needs to ClassLoaderA and loaded out by ClassLoaderA class uninstall is complete, the thread A still holding With ClassLoaderA references, however, that these classes as well as the business side loader has a clean uninstall, because the class loader to load a class and its two-way quote, which resulted in its class loader and the class can not be loaded out of the garbage collection, resulting in memory leaks. In parallel streams, ForkJoinPool and ForkJoinWorkerThreadFactory default is static and shared (JDK official recommendation to create a thread itself is relatively heavy operations, to avoid recreating ForkJoinWorkerThread a waste of resources), the following diagram depicts the scene of a memory leak:

Therefore Tomcat default SafeForkJoinWorkerThreadFactory as ForkJoinWorkerThreadFactory, and ForkJoinWorkerThread of contextClassLoader the factory creates are designated as ForkJoinPool.class.getClassLoader (), instead of the default JDK inherit the parent thread contextClassLoader, thus avoiding the Tomcat application brought by a parallel flow the class loader memory leaks.

Third, the summary

During development, if you are using a parallel stream in compute-intensive tasks, avoid dynamically load classes in subtasks; other businesses try to use the thread pool scene, rather than a parallel flow. In short, we need to avoid custom class or classes of dynamically loaded by a third party in a parallel flow Tomcat applications.

More Stay tuned  vivo Internet technology  micro-channel public number

Note: Please reprint the article with the Micro Signal: labs2020  Contact

Guess you like

Origin www.cnblogs.com/vivotech/p/11550265.html