-
-
Notifications
You must be signed in to change notification settings - Fork 804
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
1 parent
80b7c19
commit 9b53cf5
Showing
2 changed files
with
171 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
116 changes: 116 additions & 0 deletions
116
src/main/java/com/fasterxml/jackson/core/util/ThreadLocalBufferManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
package com.fasterxml.jackson.core.util; | ||
|
||
import java.lang.ref.ReferenceQueue; | ||
import java.lang.ref.SoftReference; | ||
|
||
import java.util.*; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
/** | ||
* For issue [jackson-core#400] We keep a separate Set of all SoftReferences to BufferRecyclers | ||
* which are (also) referenced using `ThreadLocals`. | ||
* We do this to be able to release them (dereference) in `releaseBuffers()` and `shutdown()` | ||
* method to reduce heap consumption during hot reloading of services where otherwise | ||
* {@link ClassLoader} would have dangling reference via {@link ThreadLocal}s. | ||
* When gc clears a SoftReference, it puts it on a newly introduced referenceQueue. | ||
* We use this queue to release the inactive SoftReferences from the Set. | ||
* | ||
* @since 2.9.6 | ||
*/ | ||
class ThreadLocalBufferManager | ||
{ | ||
/** | ||
* A lock to make sure releaseBuffers is only executed by one thread at a time | ||
* since it iterates over and modifies the allSoftBufRecyclers. | ||
*/ | ||
private final Object RELEASE_LOCK = new Object(); | ||
|
||
/** | ||
* A set of all SoftReferences to all BufferRecyclers to be able to release them on shutdown. | ||
* 'All' means the ones created by this class, in this classloader. | ||
* There may be more from other classloaders. | ||
* We use a HashSet to have quick O(1) add and remove operations. | ||
*<p> | ||
* NOTE: assumption is that {@link SoftReference} has its {@code equals()} and | ||
* {@code hashCode()} implementations defined so that they use object identity, so | ||
* we do not need to use something like {@link IdentityHashMap} | ||
*/ | ||
private final Map<SoftReference<BufferRecycler>,Boolean> _trackedRecyclers | ||
= new ConcurrentHashMap<SoftReference<BufferRecycler>, Boolean>(); | ||
|
||
/** | ||
* Queue where gc will put just-cleared SoftReferences, previously referencing BufferRecyclers. | ||
* We use it to remove the cleared softRefs from the above set. | ||
*/ | ||
private final ReferenceQueue<BufferRecycler> _refQueue = new ReferenceQueue<BufferRecycler>(); | ||
|
||
/* | ||
/********************************************************** | ||
/* Public API | ||
/********************************************************** | ||
*/ | ||
|
||
/** | ||
* Returns the lazily initialized singleton instance | ||
*/ | ||
public static ThreadLocalBufferManager instance() { | ||
return ThreadLocalBufferManagerHolder.manager; | ||
} | ||
|
||
/** | ||
* Releases the buffers retained in ThreadLocals. To be called for instance on shutdown event of applications which make use of | ||
* an environment like an appserver which stays alive and uses a thread pool that causes ThreadLocals created by the | ||
* application to survive much longer than the application itself. | ||
* It will clear all bufRecyclers from the SoftRefs and release all SoftRefs itself from our set. | ||
*/ | ||
public int releaseBuffers() { | ||
synchronized (RELEASE_LOCK) { | ||
int count = 0; | ||
// does this need to be in sync block too? Looping over Map definitely has to but... | ||
removeSoftRefsClearedByGc(); // make sure the refQueue is empty | ||
for (SoftReference<BufferRecycler> ref : _trackedRecyclers.keySet()) { | ||
ref.clear(); // possibly already cleared by gc, nothing happens in that case | ||
++count; | ||
} | ||
_trackedRecyclers.clear(); //release cleared SoftRefs | ||
return count; | ||
} | ||
} | ||
|
||
public SoftReference<BufferRecycler> wrapAndTrack(BufferRecycler br) { | ||
SoftReference<BufferRecycler> newRef; | ||
newRef = new SoftReference<BufferRecycler>(br, _refQueue); | ||
// also retain softRef to br in a set to be able to release it on shutdown | ||
_trackedRecyclers.put(newRef, true); | ||
// gc may have cleared one or more SoftRefs, clean them up to avoid a memleak | ||
removeSoftRefsClearedByGc(); | ||
return newRef; | ||
} | ||
|
||
/* | ||
/********************************************************** | ||
/* Internal methods | ||
/********************************************************** | ||
*/ | ||
|
||
/** | ||
* Remove cleared (inactive) SoftRefs from our set. Gc may have cleared one or more, | ||
* and made them inactive. We minimize contention by keeping synchronized sections short: | ||
* the poll/remove methods | ||
*/ | ||
private void removeSoftRefsClearedByGc() { | ||
SoftReference<?> clearedSoftRef; | ||
while ((clearedSoftRef = (SoftReference<?>) _refQueue.poll()) != null) { | ||
// uses reference-equality, quick, and O(1) removal by HashSet | ||
_trackedRecyclers.remove(clearedSoftRef); | ||
} | ||
} | ||
|
||
/** | ||
* ThreadLocalBufferManagerHolder uses the thread-safe initialize-on-demand, holder class idiom that implicitly | ||
* incorporates lazy initialization by declaring a static variable within a static Holder inner class | ||
*/ | ||
private static final class ThreadLocalBufferManagerHolder { | ||
static final ThreadLocalBufferManager manager = new ThreadLocalBufferManager(); | ||
} | ||
} |