Skip to content

PackageFragment.getElementName() creates a new String on every call #4930

@laeubi

Description

@laeubi

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;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions