• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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