Fixing SkyWalking ThreadPool Plugin Enhancement Failure by Making AgentClassLoader a Singleton
This article details the investigation of a SkyWalking thread‑pool plugin enhancement failure caused by multiple AgentClassLoader instances, explains the debugging steps, class‑loading behavior, and provides two practical solutions to ensure proper bytecode instrumentation for ThreadPoolExecutor in Java applications.
The author reports a failure of the SkyWalking thread‑pool plugin where the traceId was empty in asynchronous tasks. After fixing the issue, the problem and its root cause are explained.
1. Background
The project uses SkyWalking agent 8.14.0 with Spring Boot 2.2.6.RELEASE on JDK 1.8. The apm-jdk-threadpool-plugin.jar is placed in the plugins directory, but during runtime the thread‑pool enhancement does not work, resulting in missing traceId values.
2. Investigation Process
2.1 Discovery about AgentClassLoader
Logs showed that the AgentClassLoader scanned the plugins directory twice, creating two separate instances. Debugging confirmed that one instance is created during the premain method and another when the first plugin class (an Interceptor) is loaded.
Because bytecode enhancement relies on the AgentClassLoader to load Interceptor classes, the duplicate instances caused the thread‑pool plugin to fail.
2.2 ThreadPoolExecutor class‑loading timing
Using -XX:+TraceClassLoading the author observed that ThreadPoolExecutor is loaded after the transformer registration, while core classes like Runnable and Thread are loaded before the agent, making them hard to enhance.
Further debugging showed that the first instantiation of ThreadPoolExecutor occurs inside SkyWalking's logging component ( FileWriter ), which is triggered by Spring's AutowiredAnnotationBeanPostProcessor . This creates a dependency on ThreadPoolExecutor during the transform phase, preventing a second transformation.
2.3 Hypothesis on JVM transform logic
The JVM appears to avoid re‑transforming a class B that is loaded as a dependency while class A is being transformed, to prevent recursive transform loops.
2.4 Summary of findings
The thread‑pool plugin fails only when other third‑party plugins are loaded before ThreadPoolExecutor , because the latter’s enhancement depends on the AgentClassLoader which is instantiated twice, leading to duplicate scanning and logging.
3. Solution
Make AgentClassLoader a singleton to avoid repeated plugin directory scans and the extra log dependency on ThreadPoolExecutor .
Remove the logging component ( FileWriter ) that creates a ThreadPoolExecutor dependency, replacing it with a simple thread‑loop implementation.
The first approach was rejected after discussion, so the second approach was adopted, using a custom thread loop instead of Timer / TimerTask to avoid future plugin conflicts.
4. Related Issues and Discussions
https://github.com/apache/skywalking/issues/9425
https://github.com/apache/skywalking/issues/9850
https://github.com/apache/skywalking/issues/10374
https://github.com/apache/skywalking/issues/10685
https://github.com/apache/skywalking/discussions/10207
https://github.com/apache/skywalking/discussions/9888
5. Closing Remarks
The author invites readers to like, share, and follow the article, and promotes a paid knowledge community for deeper Spring and micro‑service content.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.