Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

setContextClassLoader slows down drastically when called concurrently

I have pin pointed a bottle neck in my application, it seems to me that it boils down to a call to Thread::setContextClassLoader.

Basically I was forced to mess around with the thread's context class loader due to issues with 3rd party libraries (see this question to understand why).

The solution I picked was to my knowledge the common one and it works something like this:

Thread thread = Thread.currentThread();
ClassLoader old = thread.getContextClassLoader();
thread.setContextClassLoader(newClassLoader);

try {
    ... // problematic code that uses the thread context class loader
} finally {
    thread.setContextClassLoader(old);
}

It turned out that the call to setContextClassLoader wasn't a problem when only 1 thread was running, but when more than one thread is doing it, it slows down drastically.

I've made the following test app to isolate the problem:

ArrayList<Thread> threads = new ArrayList<Thread>();
int thread_count = 1;

long start = System.currentTimeMillis();

for (int i = 0; i < thread_count; i++) {
    Thread thread = new Thread(new MyRunnable(100000000));

    thread.start();
    threads.add(thread);
}

for (Thread thread : threads) {
    thread.join();
}

long total = System.currentTimeMillis() - start;
double seconds = (double)total / 1000;

System.out.println("time in seconds: " + seconds);

And this is MyRunnable class:

public class MyRunnable implements Runnable {
    int _iterations;

    public MyRunnable(int iterations) {
        _iterations = iterations;
    }

    public void run() {
        final Thread curr = Thread.currentThread();
        final ClassLoader loader = ClassLoader.getSystemClassLoader();

        for (int i = 0; i < _iterations; i++) {
            curr.setContextClassLoader(loader);
        }
    }
}

Basically it opens a couple of threads, and sets the current threads context class loader to the system class loader in a loop.

Updated results after code change on my machine: When thread_count is 1 it finishes in half a second. 2 threads take 1.5~, 3 threads 2.7~, 4 threads 4~ - well you get the picture.

I've tried looking in Thread's implementation of setContextClassLoader and it appears that all it does is set a member variable to the class loader passed to it. I've found no locking (or access to shared resources that would require) to explain such overhead when running with more than one thread.

What am I missing here?

P.S. I'm using JRE 1.5 but the same thing happens in 1.6.

EDIT: @Tom Hawtin - See the code changes I made to rule out the reason you mentioned. Even when the system class loader is fetched once, the results are slower when the thread count > 1.

like image 673
Idan K Avatar asked Jul 24 '26 06:07

Idan K


1 Answers

The only really obvious thing in the source is not related to Thread.setContextClassLoader. ClassLoader.getSystemClassLoader calls initSystemClassLoader that locks ClassLoader.class even if the system class loader is already initialised.

A potential issue is that reading of volatile variables may have performance impact on some multiprocessor machines.

Note that we are only looking at a few hundred cycles here.

like image 123
Tom Hawtin - tackline Avatar answered Jul 25 '26 20:07

Tom Hawtin - tackline



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!