Skip to content

Ensure consistent value count in ConcurrentReferenceHashMap#Segment #31373

@nihongye

Description

@nihongye

Affects: <Spring Framework version>


affected versions:all

Background

We have a application where the jvm meta space periodically fluctuates significantly, as shown in the following screenshot.
20231006225447
After analyzing the heap space, we found a large number(200000+) of sun.reflect.DelegatingClassLoader and sun.reflect.GeneratedSerializationConstructorAccessor, which are mainly generated for org.springframework.data.redis.connection.jedis.JedisConnection$$EnhancerBySpringCGLIB$$xxxxx, because the application dynamically uses the ProxyFactory to generate proxy objects.
Further analysis revealed that the segment of the ConcurrentReferenceMap associated with org.springframework.data.redis.connection.jedis.JedisConnection$$EnhancerBySpringCGLIB$$xxxxx has a count of 0, but actually contains 1 Entry, as shown below screenshot.
1695799309403-36cc94e4-3844-4c0e-a63d-9bf21489897a
In the org.springframework.util.ConcurrentReferenceHashMap.Segment#getReference method, if the count is 0, it returns null.
20231006230508
Therefore, the org.springframework.objenesis.SpringObjenesis#cache method for the key "org.springframework.data.redis.connection.jedis.JedisConnection$$EnhancerBySpringCGLIB$$xxxxx" is ineffective, leading to the creation of a large number of sun.reflect.GeneratedSerializationConstructorAccessor repeatedly.

Why does the ConcurrentReferenceHashMap.Segment has this problem?

We have tried to reproduce the problem, as shown in the demo code
conrefmap-demo.tgz,
and it seems that the remove operation caused the problem, but there is no remove operation in the phenomenon described earlier,the org.springframework.objenesis.SpringObjenesis just invoke get and putIfAbsent.
We cannot confirm the reason for the count being less than the actual number of entries, but we speculate on the following possibilities:

  • First: in the restructure method, copy the reference, generate multiple references for same key, and some old references also have the opportunity to enter the ReferenceQueue(for example getReference has the old Reference,so may cause old Reference have the opportunity to enter the ReferenceQueue).
  • Second: when resizing, both arrays reference new and old reference, and the old references array are replaced only after the restructuring is completed. The old reference may be added to the ReferenceQueue by the garbage collector, and later, new reference are also added to the ReferenceQueue.
  • Third: mysterious multi-threading issues.

Regardless of the reason, we only need to ensure that the count is equal to the actual number of entries containing references to fix the problem.

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)type: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions