Skip to content

Commit 09c5720

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 (cherry picked from commit 2b4c81e)
1 parent 4886bf0 commit 09c5720

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,19 @@ public void clear() {
334334
}
335335
}
336336

337+
/**
338+
* Remove any entries that have been garbage collected and are no longer referenced.
339+
* Under normal circumstances garbage collected entries are automatically purged as
340+
* items are added or removed from the Map. This method can be used to force a purge,
341+
* and is useful when the Map is read frequently but updated less often.
342+
*/
343+
public void purgeUnreferencedEntries() {
344+
for (Segment segment : this.segments) {
345+
segment.restructureIfNecessary(false);
346+
}
347+
}
348+
349+
337350
@Override
338351
public int size() {
339352
int size = 0;
@@ -508,7 +521,7 @@ public void clear() {
508521
* references that have been garbage collected.
509522
* @param allowResize if resizing is permitted
510523
*/
511-
private void restructureIfNecessary(boolean allowResize) {
524+
protected final void restructureIfNecessary(boolean allowResize) {
512525
boolean needsResize = ((this.count > 0) && (this.count >= this.resizeThreshold));
513526
Reference<K, V> reference = this.referenceManager.pollForPurge();
514527
if ((reference != null) || (needsResize && allowResize)) {
@@ -546,7 +559,7 @@ private void restructureIfNecessary(boolean allowResize) {
546559
restructured[i] = null;
547560
}
548561
while (reference != null) {
549-
if (!toPurge.contains(reference)) {
562+
if (!toPurge.contains(reference) && (reference.get() != null)) {
550563
int index = getIndex(reference.getHash(), restructured);
551564
restructured[index] = this.referenceManager.createReference(
552565
reference.get(), reference.getHash(),
@@ -560,7 +573,7 @@ private void restructureIfNecessary(boolean allowResize) {
560573
if (resizing) {
561574
setReferences(restructured);
562575
}
563-
this.count = countAfterRestructure;
576+
this.count = Math.max(countAfterRestructure, 0);
564577
} finally {
565578
unlock();
566579
}
@@ -961,6 +974,7 @@ public void release() {
961974
enqueue();
962975
clear();
963976
}
977+
964978
}
965979

966980

@@ -991,6 +1005,7 @@ public void release() {
9911005
enqueue();
9921006
clear();
9931007
}
1008+
9941009
}
9951010

9961011
}

0 commit comments

Comments
 (0)