Skip to content

[Memory Leak] Static ThreadLocal<Kryo> in RedisStateMachineContextRepository causes ClassLoader leaks #1208

@QiuYucheng2003

Description

@QiuYucheng2003

Bug Description
The RedisStateMachineContextRepository class uses a private static final ThreadLocal to cache Kryo instances. However, the code never calls kryoThreadLocal.remove() after using the instance.

In a managed environment (e.g., Tomcat/Jetty) where threads are pooled and reused, this uncleaned ThreadLocal creates a strong reference chain: Thread -> ThreadLocalMap -> Kryo -> Registered Class -> ClassLoader.

This prevents the ClassLoader from being garbage collected during application redeployment, leading to Metaspace OutOfMemoryError.

Code Analysis
In src/main/java/org/springframework/statemachine/data/redis/RedisStateMachineContextRepository.java:

  1. Static ThreadLocal Definition (Line 47):
    private static final ThreadLocal kryoThreadLocal = new ThreadLocal() { ... };

  2. Usage without Cleanup (Line 90 & 103): In methods serialize and deserialize:
    Kryo kryo = kryoThreadLocal.get();
    // Use kryo...
    // MISSING: kryoThreadLocal.remove();

Impact
Context Pollution: Kryo instances may retain references to old objects or configurations from previous executions on the same thread.

Memory Leak: Severe ClassLoader leaks in web containers, eventually crashing the JVM with OOM.

Suggested Fix
Instead of a raw ThreadLocal, use the KryoPool mechanism provided by Kryo, or ensure remove() is called in a finally block.
Recommended Approach (KryoPool):
// Use com.esotericsoftware.kryo.pool.KryoPool instead of ThreadLocal
private final KryoPool pool = new KryoPool.Builder(factory).softReferences().build();

private byte[] serialize(StateMachineContext<S, E> context) {
return pool.run(kryo -> {
// serialization logic
});
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    status/need-triageTeam needs to triage and take a first look

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions