1 package com.fasterxml.jackson.core.util; 2 3 import java.lang.ref.ReferenceQueue; 4 import java.lang.ref.SoftReference; 5 6 import java.util.*; 7 import java.util.concurrent.ConcurrentHashMap; 8 9 /** 10 * For issue [jackson-core#400] We keep a separate Set of all SoftReferences to BufferRecyclers 11 * which are (also) referenced using `ThreadLocals`. 12 * We do this to be able to release them (dereference) in `releaseBuffers()` and `shutdown()` 13 * method to reduce heap consumption during hot reloading of services where otherwise 14 * {@link ClassLoader} would have dangling reference via {@link ThreadLocal}s. 15 * When gc clears a SoftReference, it puts it on a newly introduced referenceQueue. 16 * We use this queue to release the inactive SoftReferences from the Set. 17 * 18 * @since 2.9.6 19 */ 20 class ThreadLocalBufferManager 21 { 22 /** 23 * A lock to make sure releaseBuffers is only executed by one thread at a time 24 * since it iterates over and modifies the allSoftBufRecyclers. 25 */ 26 private final Object RELEASE_LOCK = new Object(); 27 28 /** 29 * A set of all SoftReferences to all BufferRecyclers to be able to release them on shutdown. 30 * 'All' means the ones created by this class, in this classloader. 31 * There may be more from other classloaders. 32 * We use a HashSet to have quick O(1) add and remove operations. 33 *<p> 34 * NOTE: assumption is that {@link SoftReference} has its {@code equals()} and 35 * {@code hashCode()} implementations defined so that they use object identity, so 36 * we do not need to use something like {@link IdentityHashMap} 37 */ 38 private final Map<SoftReference<BufferRecycler>,Boolean> _trackedRecyclers 39 = new ConcurrentHashMap<SoftReference<BufferRecycler>, Boolean>(); 40 41 /** 42 * Queue where gc will put just-cleared SoftReferences, previously referencing BufferRecyclers. 43 * We use it to remove the cleared softRefs from the above set. 44 */ 45 private final ReferenceQueue<BufferRecycler> _refQueue = new ReferenceQueue<BufferRecycler>(); 46 47 /* 48 /********************************************************** 49 /* Public API 50 /********************************************************** 51 */ 52 53 /** 54 * Returns the lazily initialized singleton instance 55 */ instance()56 public static ThreadLocalBufferManager instance() { 57 return ThreadLocalBufferManagerHolder.manager; 58 } 59 60 /** 61 * Releases the buffers retained in ThreadLocals. To be called for instance on shutdown event of applications which make use of 62 * an environment like an appserver which stays alive and uses a thread pool that causes ThreadLocals created by the 63 * application to survive much longer than the application itself. 64 * It will clear all bufRecyclers from the SoftRefs and release all SoftRefs itself from our set. 65 */ releaseBuffers()66 public int releaseBuffers() { 67 synchronized (RELEASE_LOCK) { 68 int count = 0; 69 // does this need to be in sync block too? Looping over Map definitely has to but... 70 removeSoftRefsClearedByGc(); // make sure the refQueue is empty 71 for (SoftReference<BufferRecycler> ref : _trackedRecyclers.keySet()) { 72 ref.clear(); // possibly already cleared by gc, nothing happens in that case 73 ++count; 74 } 75 _trackedRecyclers.clear(); //release cleared SoftRefs 76 return count; 77 } 78 } 79 wrapAndTrack(BufferRecycler br)80 public SoftReference<BufferRecycler> wrapAndTrack(BufferRecycler br) { 81 SoftReference<BufferRecycler> newRef; 82 newRef = new SoftReference<BufferRecycler>(br, _refQueue); 83 // also retain softRef to br in a set to be able to release it on shutdown 84 _trackedRecyclers.put(newRef, true); 85 // gc may have cleared one or more SoftRefs, clean them up to avoid a memleak 86 removeSoftRefsClearedByGc(); 87 return newRef; 88 } 89 90 /* 91 /********************************************************** 92 /* Internal methods 93 /********************************************************** 94 */ 95 96 /** 97 * Remove cleared (inactive) SoftRefs from our set. Gc may have cleared one or more, 98 * and made them inactive. We minimize contention by keeping synchronized sections short: 99 * the poll/remove methods 100 */ removeSoftRefsClearedByGc()101 private void removeSoftRefsClearedByGc() { 102 SoftReference<?> clearedSoftRef; 103 while ((clearedSoftRef = (SoftReference<?>) _refQueue.poll()) != null) { 104 // uses reference-equality, quick, and O(1) removal by HashSet 105 _trackedRecyclers.remove(clearedSoftRef); 106 } 107 } 108 109 /** 110 * ThreadLocalBufferManagerHolder uses the thread-safe initialize-on-demand, holder class idiom that implicitly 111 * incorporates lazy initialization by declaring a static variable within a static Holder inner class 112 */ 113 private static final class ThreadLocalBufferManagerHolder { 114 static final ThreadLocalBufferManager manager = new ThreadLocalBufferManager(); 115 } 116 } 117