PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/jira-project/jira-components/jira-core/src/main/java/com/atlassian/jira/util/cache/WeakInterner.java

https://bitbucket.org/ahmed_bilal_360factors/jira7-core
Java | 187 lines | 89 code | 28 blank | 70 comment | 13 complexity | 876c62236200291899571fa9012f9ea5 MD5 | raw file
Possible License(s): Apache-2.0
  1. package com.atlassian.jira.util.cache;
  2. import javax.annotation.Nonnull;
  3. import javax.annotation.Nullable;
  4. import java.lang.ref.Reference;
  5. import java.lang.ref.ReferenceQueue;
  6. import java.lang.ref.WeakReference;
  7. import java.util.concurrent.ConcurrentHashMap;
  8. import java.util.concurrent.ConcurrentMap;
  9. import static com.atlassian.jira.util.dbc.Assertions.notNull;
  10. /**
  11. * Similar to a Guava {@code WeakInterner}, but significantly lighter weight.
  12. * <p>
  13. * The Guava implementation of a weak interner has considerable overhead because it is implemented in terms of
  14. * the {@code CustomConcurrentHashMap} grab bag of functionality with all its accompanying statistical tracking,
  15. * eviction policy, and so on. This implementation is based directly on the lighter {@code ConcurrentHashMap}
  16. * implementation that comes with the JDK.
  17. * </p>
  18. * <p>
  19. * Note: This assumes that your interned objects are sane for interning purposes, meaning that they are
  20. * immutable objects with stable hash codes that are consistent with equals and have a reasonably efficient
  21. * implementation for {@link Object#equals(Object) equals}. Violate these assumptions at your own risk.
  22. * </p>
  23. *
  24. * @since v6.3
  25. */
  26. public class WeakInterner<T> {
  27. private static final int DEFAULT_INITIAL_CAPACITY = 16;
  28. private static final float DEFAULT_LOAD_FACTOR = 0.75f;
  29. private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
  30. /**
  31. * The real backing store for the interner.
  32. */
  33. final ConcurrentMap<InternReference<T>, InternReference<T>> store;
  34. /**
  35. * Weak references that are cleared by the GC get enqueued here so that we'll know to evict them in cleanUp().
  36. */
  37. final ReferenceQueue<T> queue;
  38. public static <T> WeakInterner<T> newWeakInterner() {
  39. return new WeakInterner<T>();
  40. }
  41. public WeakInterner() {
  42. this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
  43. }
  44. public WeakInterner(final int initialCapacity) {
  45. this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
  46. }
  47. public WeakInterner(final int initialCapacity, final float loadFactor) {
  48. this(initialCapacity, loadFactor, DEFAULT_CONCURRENCY_LEVEL);
  49. }
  50. public WeakInterner(final int initialCapacity, final float loadFactor, final int concurrencyLevel) {
  51. this.store = new ConcurrentHashMap<InternReference<T>, InternReference<T>>(initialCapacity, loadFactor, concurrencyLevel);
  52. this.queue = new ReferenceQueue<T>();
  53. }
  54. /**
  55. * Weakly interns the specified non-null value.
  56. * <p>
  57. * Implicitly calls {@link #cleanUp()}.
  58. * </p>
  59. *
  60. * @param value the value to intern; must not be {@code null}
  61. * @return either another object that was previously interned and is semantically equal to {@code value}, or
  62. * {@code value} itself if there is no previously interned instance available.
  63. * @throws IllegalArgumentException if {@code value} is {@code null}
  64. */
  65. @Nonnull
  66. public T intern(@Nonnull final T value) {
  67. return internImpl(notNull("value", value));
  68. }
  69. /**
  70. * Weakly interns the specified value, with {@code null} values tolerated.
  71. * <p>
  72. * Implicitly calls {@link #cleanUp()} unless {@code value} is {@code null}.
  73. * </p>
  74. *
  75. * @param value the value to intern; may be {@code null}
  76. * @return either {@code value} or another instance of that object which was previously interned and is
  77. * semantically equal to {@code value}
  78. */
  79. @Nullable
  80. public T internOrNull(@Nullable final T value) {
  81. return (value != null) ? internImpl(value) : null;
  82. }
  83. /**
  84. * Requests an explicit clean-up pass.
  85. * <p>
  86. * The clean-up is invoked implicitly on every call to {@link #intern(Object)} or {@link #internOrNull(Object)}
  87. * (provided the argument is not in fact {@code null}), so it should not normally be necessary to call this
  88. * method explicitly.
  89. * </p>
  90. */
  91. public void cleanUp() {
  92. Reference<? extends T> dead = queue.poll();
  93. while (dead != null) {
  94. // Our "InternReference" objects are the only things that go in our ref queue, so the type check
  95. // would be redundant.
  96. //noinspection SuspiciousMethodCalls
  97. store.remove(dead);
  98. dead = queue.poll();
  99. }
  100. }
  101. @Nonnull
  102. private T internImpl(@Nonnull final T value) {
  103. cleanUp();
  104. final InternReference<T> ref = new InternReference<T>(value, queue);
  105. for (; ; ) {
  106. // If we are first, then our own value becomes the interned one
  107. final InternReference<T> existing = store.putIfAbsent(ref, ref);
  108. if (existing == null) {
  109. return value;
  110. }
  111. // Data race: GC can sneak in to clear the reference between the keys testing as equal and us using get()
  112. // to create a new strong reference to the interned value. We must null check the interned value to make
  113. // sure that this hasn't happened.
  114. final T interned = existing.get();
  115. if (interned != null) {
  116. return interned;
  117. }
  118. // This shouldn't really be necessary as the null value should guarantee that it won't pass the equals
  119. // test on the next attempt; however, this is a rare and tiny extra cost (the next cleanUp() will do a
  120. // redundant remove() for this key) for the piece of mind that this dead key can't possibly get in our
  121. // way again.
  122. store.remove(existing);
  123. }
  124. }
  125. static final class InternReference<T> extends WeakReference<T> {
  126. private final int hash;
  127. InternReference(final T value, final ReferenceQueue<? super T> queue) {
  128. super(value, queue);
  129. this.hash = value.hashCode();
  130. }
  131. /**
  132. * Keys are equal by identity or by non-null interned value.
  133. * <p>
  134. * While the interned value is still reachable, a new key with the same value will be equal to it and find
  135. * the same entry. However, once the GC has cleared the reference, it will only be equal via identity. The
  136. * hash code of the key is the same as that of the interned value and is held locally so that it will remain
  137. * stable after the weak reference is cleared.
  138. */
  139. @Override
  140. public boolean equals(final Object obj) {
  141. return this == obj || (obj instanceof InternReference && equals((InternReference<?>) obj));
  142. }
  143. private boolean equals(@Nonnull final InternReference<?> other) {
  144. final T value = get();
  145. return value != null && value.equals(other.get());
  146. }
  147. @Override
  148. public int hashCode() {
  149. return hash;
  150. }
  151. @Override
  152. public String toString() {
  153. return "InternReference@" + Integer.toHexString(System.identityHashCode(this)) +
  154. "[hash=" + hash +
  155. ",referent=" + get() +
  156. ']';
  157. }
  158. }
  159. }