Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Deadlock in ClassLoaders

I have written two custom class loaders to load code dynamically.

The first one does load code from a Jar:

package com.customweb.build.bean.include;

import java.net.URL;
import java.net.URLClassLoader;

import com.customweb.build.process.ILeafClassLoader;

public class JarClassLoader extends URLClassLoader implements ILeafClassLoader {

    public JarClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }

    @Override
    public Class<?> findClassWithoutCycles(String name) throws ClassNotFoundException {

        Class<?> c = findLoadedClass(name);
        if (c != null) {
            return c;
        }

        return findClass(name);
    }

    @Override
    protected Class<?> findClass(String qualifiedClassName) throws ClassNotFoundException {
        synchronized (this.getParent()) {
            synchronized (this) {
                return super.findClass(qualifiedClassName);
            }
        }
    }

    @Override
    public URL findResourceWithoutCycles(String name) {
        return super.findResource(name);
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        synchronized (this.getParent()) {
            synchronized (this) {
                return super.loadClass(name);
            }
        }
    }

}

The other class loader takes multiple class loaders to allow to access the classes of the other class loaders. During the initialization of the first one, I set an instance of this class loader as the parent. To break the cycle I use the method 'findClassWithoutCycles'.

package com.customweb.build.process;

import java.net.URL;
import java.security.SecureClassLoader;
import java.util.ArrayList;
import java.util.List;

public class MultiClassLoader extends SecureClassLoader {

    private final List<ClassLoader> classLoaders = new ArrayList<ClassLoader>();

    public MultiClassLoader(ClassLoader parent) {
        super(parent);
    }

    public void addClassLoader(ClassLoader loader) {
        this.classLoaders.add(loader);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {

        for (ClassLoader loader : classLoaders) {
            try {
                if (loader instanceof ILeafClassLoader) {
                    return ((ILeafClassLoader) loader).findClassWithoutCycles(name);
                } else {
                    return loader.loadClass(name);
                }
            } catch (ClassNotFoundException e) {
                // Ignore it, we try the next class loader.
            }
        }

        throw new ClassNotFoundException(name);
    }

    @Override
    protected URL findResource(String name) {

        for (ClassLoader loader : classLoaders) {
            URL url = null;
            if (loader instanceof ILeafClassLoader) {
                url = ((ILeafClassLoader) loader).findResourceWithoutCycles(name);
            } else {
                url = loader.getResource(name);
            }

            if (url != null) {
                return url;
            }
        }

        return null;
    }
}

But when I use this class loaders I get most of the time a deadlock. I have past here the thread dump: http://pastebin.com/6wZKv4Y0

Since the Java ClassLoader blocks in some methods the thread by synchronizing on $this, I try to synchronizing on the MultiClassLoader first and then on the JarClassLoader. This should prevent any deadlocks, when the order of acquiring a lock is the same. But it seems as somewhere in the native class loading routine a lock on the class loader is acquired. I came to this conclusion because the thread 'pool-2-thread-8' is locked on the object '0x00000007b0f7f710'. But in the log I can't see when this lock is acquired and by which thread.

How can I find out which thread does the synchronizing on the classloader?

Edit: I solved it by synchronizing on all class loaders before invoking the loadClass of the MultiClassLoader.

like image 561
Thomas Hunziker Avatar asked Sep 05 '13 16:09

Thomas Hunziker


People also ask

What is class loading deadlock?

The deadlock can only occur if you have 2 threads and one starts to load User and one starts to load NonRegisteredUser . There are synchronizations in place that will cause a deadlock, but then it requires separate threads. If the loading happens in a single thread, there is no deadlock as the thread owns both locks.

What are the different Classloaders in Java?

As we can see, there are three different class loaders here: application, extension, and bootstrap (displayed as null). The application class loader loads the class where the example method is contained.

How many types of Classloaders are there in Java?

There are three types of built-in ClassLoader in Java. Bootstrap Class Loader – It loads JDK internal classes. It loads rt. jar and other core classes for example java.

Why do we need custom Classloaders in Java?

There are three main reasons why you might want to write your own class loader. To allow class loading from alternative repositories. This is the most common case, in which an application developer might want to load classes from other locations, for example, over a network connection. To partition user code.


1 Answers

The JVM acquires a lock on the ClassLoader before it invokes loadClass. This happens if class loaded via one of your JarClassLoader references another class and the JVM tries to resolve that reference. It will go directly to the ClassLoader which created the class, lock it and invoke loadClass. But then you are trying to get a lock on the parent loader before locking the JarClassLoader again. So the ordering of the two locks does not work.

But I don’t see reasons for any of the two locks as you do not access any resources requiring synchronization. The inherited internal state of the URLClassLoader is maintained by its implementation itself.

However if you want to add more state to your classes requiring synchronization you should use different mechanisms as locking the ClassLoader instances.

http://docs.oracle.com/javase/7/docs/technotes/guides/lang/cl-mt.html says

If you have a custom class loader with a risk of deadlocking, with the Java SE 7 release, you can avoid deadlocks by following these rules:

  1. Ensure that your custom class loader is multithread safe for concurrent class loading.

    a. Decide upon an internal locking scheme. For example, java.lang.ClassLoader uses a locking scheme based on the requested class name.

    b. Remove all synchronization on the class loader object lock alone.

    c. Ensure that critical sections are safe for multiple threads loading different classes.

  2. In your custom class loader's static initializer, invoke java.lang.ClassLoader's static method registerAsParallelCapable(). This registration indicates that all instances of your custom class loader are multithread safe.

  3. Check that all class loader classes that this custom class loader extends also invoke the registerAsParallelCapable() method in their class initializers. Ensure that they are multithread safe for concurrent class loading.

If your custom class loader overrides only findClass(String), you do not need further changes. This is the recommended mechanism to create a custom class loader.

like image 126
Holger Avatar answered Oct 14 '22 10:10

Holger