Why JaCoCo Can Break Your Java Service: ClassCastException Explained and Fixed
After integrating JaCoCo, a Java service threw a ClassCastException during order processing due to the synthetic $jacocoData boolean array, and the article details the root cause, code analysis, synthetic fields, JaCoCo probe insertion, and a robust fix using field.isSynthetic() filtering.
Problem Description
JaCoCo is a widely used open‑source coverage tool. After adding it to the test environment, the machine starts normally, but the order‑placement process throws an exception, blocking the workflow.
Removing the JaCoCo configuration, recompiling and redeploying restores normal order processing. The stack trace shows a
ClassCastExceptioncaused by a failed type conversion:
[Z cannot be cast to [Ljava.lang.Object;.
Stack Trace
<code>java.lang.ClassCastException: [Z cannot be cast to [Ljava.lang.Object;
at com.jd.**.TdeProxy.encryptObject(TdeProxy.java:93)
at com.jd.**.TdeProxy.encryptObject(TdeProxy.java:133)
at com.jd.**.TdeProxy.encryptObject(TdeProxy.java:90)
... (omitted)
</code>Analysis
1. Error Code
The error occurs when the code attempts to cast a
boolean[](represented as
[Zin JVM signatures) to an
Object[]. This happens in the
encryptObjectmethod during array handling.
2. Analysis Path
The
encryptObjectmethod uses recursion to traverse collections. The problem lies in the element set that cannot be cast correctly, which points to the
getDeclaredFieldsAllmethod that collects class fields.
<code>public Field[] getDeclaredFieldsAll(Class clazz) {
List<Field> fieldsList = new ArrayList<>();
while (clazz != null) {
Field[] declaredFields = clazz.getDeclaredFields();
fieldsList.addAll(Arrays.asList(declaredFields));
clazz = clazz.getSuperclass();
}
return fieldsList.toArray(new Field[fieldsList.size()]);
}
</code>JaCoCo injects a synthetic field named
$jacocoData(a
boolean[]) into every class. When the reflection utility processes this field, the cast to
Object[]fails, producing the observed exception.
3. Synthetic Fields and Methods
Synthetic members are generated by the Java compiler to support internal language features. For example, accessing a private field of an inner class from the outer class creates a synthetic accessor method.
<code>public class Pack {
public static void main(String[] args) {
Pack.Goods goods = new Pack.Goods();
System.out.println(goods.name);
}
private static class Goods {
private String name = "手机";
}
}
</code>After compilation, the compiler adds a method like
access$100to allow the outer class to read the private
namefield. Such synthetic methods appear in bytecode but are not part of the source code.
4. JaCoCo Principle
JaCoCo uses ASM to insert probe booleans into bytecode. Each probe is a
booleanstored in the generated
$jacocoDataarray. The probes record whether a piece of code has been executed without altering program logic.
Solution
Filter out synthetic fields when gathering class fields. The
Field.isSynthetic()method identifies compiler‑generated members.
<code>List<Field> fieldsList = Arrays.stream(declaredFields)
.filter(field -> !field.isSynthetic())
.collect(Collectors.toList());
</code>After applying this filter and redeploying with JaCoCo enabled, the order‑placement functionality works correctly.
Conclusion
This article records a troubleshooting case where adding JaCoCo introduced a runtime
ClassCastExceptiondue to the synthetic
$jacocoDatafield. Developers using reflection or annotation‑based tools should be aware of synthetic members and filter them to improve code robustness.
JD Cloud Developers
JD Cloud Developers (Developer of JD Technology) is a JD Technology Group platform offering technical sharing and communication for AI, cloud computing, IoT and related developers. It publishes JD product technical information, industry content, and tech event news. Embrace technology and partner with developers to envision the future.
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.