com.fasterxml.jackson.jr.private_.util.ThreadLocalBufferManager Maven / Gradle / Ivy
Show all versions of jackson-jr-all Show documentation
package com.fasterxml.jackson.core.util;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
/**
* 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 ReentrantLock RELEASE_LOCK = new ReentrantLock();
/**
* 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.
*
* 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,Boolean> _trackedRecyclers
= new ConcurrentHashMap<>();
/**
* 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 _refQueue = new ReferenceQueue<>();
/*
/**********************************************************
/* 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() {
int count = 0;
RELEASE_LOCK.lock();
try {
// 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 ref : _trackedRecyclers.keySet()) {
ref.clear(); // possibly already cleared by gc, nothing happens in that case
++count;
}
_trackedRecyclers.clear(); //release cleared SoftRefs
} finally {
RELEASE_LOCK.unlock();
}
return count;
}
public SoftReference wrapAndTrack(BufferRecycler br) {
SoftReference newRef;
newRef = new SoftReference<>(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.
*/
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();
}
}