During analysis of two heapdumps shared here the following issue was discovered as a hotspot that can benefit from optimization:
Performance Data
| Metric |
WITH transitive |
WITHOUT transitive |
Ratio |
| getElementName() self time (µs) |
3,463,714 |
1,325,883 |
2.6× |
| toStringInfo() self time (µs) |
1,555,866 |
504,270 |
3.1× |
| Util.concatWith() (µs) |
related |
related |
— |
| StringBuilder.ensureCapacity (µs) |
9,432,131 |
4,138,082 |
2.3× |
Description
PackageFragment.getElementName() concatenates the names array with a dot separator on every invocation:
public String getElementName() {
if (this.names.length == 0)
return DEFAULT_PACKAGE_NAME;
return Util.concatWith(this.names, '.'); // creates new StringBuilder + String each time
}
Where Util.concatWith() creates a new StringBuilder and iterates:
public static final String concatWith(String[] array, char separator) {
StringBuilder buffer = new StringBuilder();
for (int i = 0, length = array.length; i < length; i++) {
buffer.append(array[i]);
if (i < length - 1) buffer.append(separator);
}
return buffer.toString();
}
The PackageFragment.names array is immutable after construction. With transitive dependencies, there are far more PackageFragment objects, and getElementName() is called repeatedly during classpath traversal, dependency resolution, and builder operations. The per-thread data shows one worker thread accumulated significant time in toStringInfo() → getElementName() → string building.
Suggested Fix
Cache the concatenated element name as a field in PackageFragment:
private volatile String cachedElementName;
public String getElementName() {
if (this.names.length == 0)
return DEFAULT_PACKAGE_NAME;
String name = this.cachedElementName;
if (name == null) {
name = Util.concatWith(this.names, '.');
this.cachedElementName = name;
}
return name;
}
During analysis of two heapdumps shared here the following issue was discovered as a hotspot that can benefit from optimization:
Performance Data
Description
PackageFragment.getElementName()concatenates thenamesarray with a dot separator on every invocation:Where
Util.concatWith()creates a newStringBuilderand iterates:The
PackageFragment.namesarray is immutable after construction. With transitive dependencies, there are far morePackageFragmentobjects, andgetElementName()is called repeatedly during classpath traversal, dependency resolution, and builder operations. The per-thread data shows one worker thread accumulated significant time intoStringInfo()→getElementName()→ string building.Suggested Fix
Cache the concatenated element name as a field in
PackageFragment: