Skip to content

Commit 2b4c81e

Browse files
author
Phillip Webb
committed
Fix memory leak in ConcurrentReferenceHashMap
Update ConcurrentReferenceHashMap to protect against references that have been garbage collected but for some reason do not appear as a `pollForPurge` result. Also added purgeUnreferencedEntries() method to allow for programmatic cleanup. Issue: SPR-11440
1 parent fe6a982 commit 2b4c81e

File tree

1 file changed

+18
-3
lines changed

1 file changed

+18
-3
lines changed

spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java

+18-3
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,19 @@ public void clear() {
338338
}
339339
}
340340

341+
/**
342+
* Remove any entries that have been garbage collected and are no longer referenced.
343+
* Under normal circumstances garbage collected entries are automatically purged as
344+
* items are added or removed from the Map. This method can be used to force a purge,
345+
* and is useful when the Map is read frequently but updated less often.
346+
*/
347+
public void purgeUnreferencedEntries() {
348+
for (Segment segment : this.segments) {
349+
segment.restructureIfNecessary(false);
350+
}
351+
}
352+
353+
341354
@Override
342355
public int size() {
343356
int size = 0;
@@ -512,7 +525,7 @@ public void clear() {
512525
* references that have been garbage collected.
513526
* @param allowResize if resizing is permitted
514527
*/
515-
private void restructureIfNecessary(boolean allowResize) {
528+
protected final void restructureIfNecessary(boolean allowResize) {
516529
boolean needsResize = ((this.count > 0) && (this.count >= this.resizeThreshold));
517530
Reference<K, V> reference = this.referenceManager.pollForPurge();
518531
if ((reference != null) || (needsResize && allowResize)) {
@@ -550,7 +563,7 @@ private void restructureIfNecessary(boolean allowResize) {
550563
restructured[i] = null;
551564
}
552565
while (reference != null) {
553-
if (!toPurge.contains(reference)) {
566+
if (!toPurge.contains(reference) && (reference.get() != null)) {
554567
int index = getIndex(reference.getHash(), restructured);
555568
restructured[index] = this.referenceManager.createReference(
556569
reference.get(), reference.getHash(),
@@ -564,7 +577,7 @@ private void restructureIfNecessary(boolean allowResize) {
564577
if (resizing) {
565578
setReferences(restructured);
566579
}
567-
this.count = countAfterRestructure;
580+
this.count = Math.max(countAfterRestructure, 0);
568581
} finally {
569582
unlock();
570583
}
@@ -974,6 +987,7 @@ public void release() {
974987
enqueue();
975988
clear();
976989
}
990+
977991
}
978992

979993

@@ -1007,6 +1021,7 @@ public void release() {
10071021
enqueue();
10081022
clear();
10091023
}
1024+
10101025
}
10111026

10121027
}

0 commit comments

Comments
 (0)