Skip to content

PropertyNamingStrategy class initialization depends on its subclass, this can lead to class loading deadlock #2715

@fangwentong

Description

@fangwentong

If a class refer to its subclasses in its static initializers or in static fields, this references can cause JVM-level deadlocks in multithreaded environment, when one thread tries to load superclass and another thread tries to load subclass at the same time.

See: https://bugs.openjdk.java.net/browse/JDK-8037567

The following demo code can reproduce this deadlock

public class ClassInitDeadLock {

    public static void main(String[] args) throws InterruptedException {
        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                // trigger supper class initialize
                PascalCaseStrategy strategy = PropertyNamingStrategy.PASCAL_CASE_TO_CAMEL_CASE;
            }
        }, "Thread-A");

        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                // trigger sub class initialize
                PascalCaseStrategy strategy = new PascalCaseStrategy();
            }
        }, "Thread-B");

        threadA.start();
        Thread.sleep(100);
        threadB.start();
    }

    public static class PropertyNamingStrategy {
        static {
            try {
                // sleep 1s for deadlock reproduction
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // ignore
            }
        }

        public static final PascalCaseStrategy PASCAL_CASE_TO_CAMEL_CASE = new PascalCaseStrategy();
    }

    public static class PascalCaseStrategy extends PropertyNamingStrategy {
    }
}

When the demo program ClassInitDeadLock started, it could not exit automatically. Thread-A holding PropertyNamingStrategy class , and waiting PascalCaseStrategy class initialize, while Thread-B holding PascalCaseStrategy class, and waiting PropertyNamingStrategy class initialize, deadlock occurred!

jstack of these two thread:

"Thread-A" #11 prio=5 os_prio=31 tid=0x00007fa8c29d8000 nid=0xa803 in Object.wait() [0x000070000a4c4000]
   java.lang.Thread.State: RUNNABLE
	at ClassInitDeadLock$PropertyNamingStrategy.<clinit>(ClassInitDeadLock.java:35)
	at ClassInitDeadLock$1.run(ClassInitDeadLock.java:8)
	at java.lang.Thread.run(Thread.java:748)

"Thread-B" #12 prio=5 os_prio=31 tid=0x00007fa8c4a9b800 nid=0x5603 in Object.wait() [0x000070000a5c7000]
   java.lang.Thread.State: RUNNABLE
	at ClassInitDeadLock$2.run(ClassInitDeadLock.java:16)
	at java.lang.Thread.run(Thread.java:748)

In the actual environment, PropertyNamingStrategy and its subclass defined in jackson-databind initialize very fast, so It's hard to produce the scene that multithread load superclass and subclass at the same time.

I happened to meet this deadlock recently, It turn out to be JsonErrorUnmarshaller.java#L37 defined in aws-java-sdk-core, trying to initialize subclass PascalCaseStrategy, at the same time, another thread is initilizing superclass PropertyNamingStrategy

private static final ObjectMapper MAPPER = new ObjectMapper().configure(
        DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).setPropertyNamingStrategy(
        new PascalCaseStrategy());

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions