Skip to content

Conversation

@belugabehr
Copy link

Replace Collections.emptyMap() with new IdentityHashMap to maintain consistent map implementation type throughout the object's lifecycle, enabling JVM scalar replacement optimization and reducing iterator allocation overhead.

Fixes gh-6811

@belugabehr belugabehr force-pushed the belugabehr/composite-id-map branch 2 times, most recently from 7ea05ba to 3edc0a9 Compare November 1, 2025 02:56
@belugabehr belugabehr force-pushed the belugabehr/composite-id-map branch 2 times, most recently from a55c2da to 1e39d3f Compare November 18, 2025 03:34
@belugabehr
Copy link
Author

belugabehr commented Nov 18, 2025

@jonatan-ivanov - Made suggested change. Confirmed the allocations have stopped: #6811 (comment)

@shakuzen
Copy link
Member

@belugabehr could you share the code for the benchmark you're using?

@belugabehr
Copy link
Author

@shakuzen Details are posted in: gh-6811

Thanks!

Copy link
Member

@shakuzen shakuzen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this change is fine as is, but it's also brittle. I wonder if we should try adding a test that asserts there is no allocation after C2 compilation. The problem is that we only have heuristics to guess when C2 compilation is done - by default in most modern JVMs it starts after 10,000 invocations of a method.

It may be helpful to commit the benchmarks along with this, but benchmarks don't get run with the build so they are not a guarantee this won't regress with some other change.

I think there is larger refactoring to consider around this, but I think it's okay to consider that after merging this.

I also think we should pursue adding a warning log so that users who can do all setup before registering metrics can avoid surprises like this and others (such as the values being recorded before a non-composite registry is added being dropped).


private Map<MeterRegistry, T> children = Collections.emptyMap();
@SuppressWarnings("unchecked")
private Map<MeterRegistry, T> children = (Map<MeterRegistry, T>) EMPTY_CHILDREN;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to ensure a specific type for optimizations, it's probably best to use the specific type on the left-hand side here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static values like this cannot be templated and require this bridge. Please consider this PR as-is. If you would like a different variation, would you mind creating a new PR with whatever you think is best in order to streamline the back-and-forth? Thanks!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private Map<MeterRegistry, T> children = (Map<MeterRegistry, T>) EMPTY_CHILDREN;
private IdentityHashMap<MeterRegistry, T> children = (IdentityHashMap<MeterRegistry, T>) EMPTY_CHILDREN;

My comment wasn't clear enough. I should have used the code suggestion feature, but other changes are required along with it. Anyway, we can take care of it as polish when merging. I thought it was worth bringing up in review, though.

@belugabehr
Copy link
Author

I also think we should pursue adding a warning log so that users who can do all setup before registering metrics can avoid surprises like this and others (such as the values being recorded before a non-composite registry is added being dropped).

Agreed!

@shakuzen
Copy link
Member

I also think we should pursue adding a warning log so that users who can do all setup before registering metrics can avoid surprises like this and others (such as the values being recorded before a non-composite registry is added being dropped).

I've opened #6908 for this.

…iteMeter

Replace Collections.emptyMap() with new IdentityHashMap<>() to maintain
consistent map implementation type throughout the object's lifecycle,
enabling JVM scalar replacement optimization and reducing iterator
allocation overhead.

Fixes micrometer-metricsgh-6811

Signed-off-by: David Mollitor <[email protected]>
@belugabehr belugabehr force-pushed the belugabehr/composite-id-map branch from 1e39d3f to 23bd25c Compare November 20, 2025 16:40
@belugabehr
Copy link
Author

@shakuzen - PR updated with your suggestions. Thanks for all the help! Excited to get this over the line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Collections Empty Map Defeats JIT Scalar Replacement for AbstractCompositeMeter

3 participants