Skip to content

Commit e3a86be

Browse files
philwebbjhoeller
authored andcommitted
Make ConcurrentReferenceHashMap more JIT friendly
Update `ConcurrentReferenceHashMap` to make some methods more inline friendly, and to manually inline a few others. These minor optimizations don't make a great deal of difference for most applications, but seem worthwhile since we use `ConcurrentReferenceHashMap` for many internal caches. Closes gh-22566
1 parent aa4e56b commit e3a86be

File tree

1 file changed

+63
-59
lines changed

1 file changed

+63
-59
lines changed

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

+63-59
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
import java.util.concurrent.locks.ReentrantLock;
3636

3737
import org.springframework.lang.Nullable;
38+
import org.springframework.util.ConcurrentReferenceHashMap.Reference;
39+
import org.springframework.util.ConcurrentReferenceHashMap.Restructure;
3840

3941
/**
4042
* A {@link ConcurrentHashMap} that uses {@link ReferenceType#SOFT soft} or
@@ -232,27 +234,24 @@ protected int getHash(@Nullable Object o) {
232234
@Override
233235
@Nullable
234236
public V get(@Nullable Object key) {
235-
Entry<K, V> entry = getEntryIfAvailable(key);
237+
Reference<K, V> ref = getReference(key, Restructure.WHEN_NECESSARY);
238+
Entry<K, V> entry = (ref != null ? ref.get() : null);
236239
return (entry != null ? entry.getValue() : null);
237240
}
238241

239242
@Override
240243
@Nullable
241244
public V getOrDefault(@Nullable Object key, @Nullable V defaultValue) {
242-
Entry<K, V> entry = getEntryIfAvailable(key);
245+
Reference<K, V> ref = getReference(key, Restructure.WHEN_NECESSARY);
246+
Entry<K, V> entry = (ref != null ? ref.get() : null);
243247
return (entry != null ? entry.getValue() : defaultValue);
244248
}
245249

246250
@Override
247251
public boolean containsKey(@Nullable Object key) {
248-
Entry<K, V> entry = getEntryIfAvailable(key);
249-
return (entry != null && ObjectUtils.nullSafeEquals(entry.getKey(), key));
250-
}
251-
252-
@Nullable
253-
private Entry<K, V> getEntryIfAvailable(@Nullable Object key) {
254252
Reference<K, V> ref = getReference(key, Restructure.WHEN_NECESSARY);
255-
return (ref != null ? ref.get() : null);
253+
Entry<K, V> entry = (ref != null ? ref.get() : null);
254+
return (entry != null && ObjectUtils.nullSafeEquals(entry.getKey(), key));
256255
}
257256

258257
/**
@@ -573,65 +572,70 @@ public void clear() {
573572
*/
574573
protected final void restructureIfNecessary(boolean allowResize) {
575574
int currCount = this.count.get();
576-
boolean needsResize = (currCount > 0 && currCount >= this.resizeThreshold);
575+
boolean needsResize = allowResize && (currCount > 0 && currCount >= this.resizeThreshold);
577576
Reference<K, V> ref = this.referenceManager.pollForPurge();
578-
if (ref != null || (needsResize && allowResize)) {
579-
lock();
580-
try {
581-
int countAfterRestructure = this.count.get();
582-
Set<Reference<K, V>> toPurge = Collections.emptySet();
583-
if (ref != null) {
584-
toPurge = new HashSet<>();
585-
while (ref != null) {
586-
toPurge.add(ref);
587-
ref = this.referenceManager.pollForPurge();
588-
}
589-
}
590-
countAfterRestructure -= toPurge.size();
591-
592-
// Recalculate taking into account count inside lock and items that
593-
// will be purged
594-
needsResize = (countAfterRestructure > 0 && countAfterRestructure >= this.resizeThreshold);
595-
boolean resizing = false;
596-
int restructureSize = this.references.length;
597-
if (allowResize && needsResize && restructureSize < MAXIMUM_SEGMENT_SIZE) {
598-
restructureSize <<= 1;
599-
resizing = true;
577+
if (ref != null || (needsResize)) {
578+
restructure(allowResize, ref);
579+
}
580+
}
581+
582+
private void restructure(boolean allowResize, Reference<K, V> ref) {
583+
boolean needsResize;
584+
lock();
585+
try {
586+
int countAfterRestructure = this.count.get();
587+
Set<Reference<K, V>> toPurge = Collections.emptySet();
588+
if (ref != null) {
589+
toPurge = new HashSet<>();
590+
while (ref != null) {
591+
toPurge.add(ref);
592+
ref = this.referenceManager.pollForPurge();
600593
}
594+
}
595+
countAfterRestructure -= toPurge.size();
596+
597+
// Recalculate taking into account count inside lock and items that
598+
// will be purged
599+
needsResize = (countAfterRestructure > 0 && countAfterRestructure >= this.resizeThreshold);
600+
boolean resizing = false;
601+
int restructureSize = this.references.length;
602+
if (allowResize && needsResize && restructureSize < MAXIMUM_SEGMENT_SIZE) {
603+
restructureSize <<= 1;
604+
resizing = true;
605+
}
601606

602-
// Either create a new table or reuse the existing one
603-
Reference<K, V>[] restructured =
604-
(resizing ? createReferenceArray(restructureSize) : this.references);
607+
// Either create a new table or reuse the existing one
608+
Reference<K, V>[] restructured =
609+
(resizing ? createReferenceArray(restructureSize) : this.references);
605610

606-
// Restructure
607-
for (int i = 0; i < this.references.length; i++) {
608-
ref = this.references[i];
609-
if (!resizing) {
610-
restructured[i] = null;
611-
}
612-
while (ref != null) {
613-
if (!toPurge.contains(ref)) {
614-
Entry<K, V> entry = ref.get();
615-
if (entry != null) {
616-
int index = getIndex(ref.getHash(), restructured);
617-
restructured[index] = this.referenceManager.createReference(
618-
entry, ref.getHash(), restructured[index]);
619-
}
611+
// Restructure
612+
for (int i = 0; i < this.references.length; i++) {
613+
ref = this.references[i];
614+
if (!resizing) {
615+
restructured[i] = null;
616+
}
617+
while (ref != null) {
618+
if (!toPurge.contains(ref)) {
619+
Entry<K, V> entry = ref.get();
620+
if (entry != null) {
621+
int index = getIndex(ref.getHash(), restructured);
622+
restructured[index] = this.referenceManager.createReference(
623+
entry, ref.getHash(), restructured[index]);
620624
}
621-
ref = ref.getNext();
622625
}
626+
ref = ref.getNext();
623627
}
624-
625-
// Replace volatile members
626-
if (resizing) {
627-
this.references = restructured;
628-
this.resizeThreshold = (int) (this.references.length * getLoadFactor());
629-
}
630-
this.count.set(Math.max(countAfterRestructure, 0));
631628
}
632-
finally {
633-
unlock();
629+
630+
// Replace volatile members
631+
if (resizing) {
632+
this.references = restructured;
633+
this.resizeThreshold = (int) (this.references.length * getLoadFactor());
634634
}
635+
this.count.set(Math.max(countAfterRestructure, 0));
636+
}
637+
finally {
638+
unlock();
635639
}
636640
}
637641

0 commit comments

Comments
 (0)