Skip to content

Commit 24f65a2

Browse files
committed
Merge branch '2.9'
2 parents 465fd8e + 9b53cf5 commit 24f65a2

File tree

3 files changed

+176
-2
lines changed

3 files changed

+176
-2
lines changed

release-notes/VERSION-2.x

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ JSON library.
1414
=== Releases ===
1515
------------------------------------------------------------------------
1616

17-
2.9.4 (24-Jan-2017)
17+
2.9.5 (26-Mar-2018)
18+
19+
No changes since 2.9.4
20+
21+
2.9.4 (24-Jan-2018)
1822

1923
#414: Base64 MIME variant does not ignore white space chars as per RFC2045
2024
(reported by tmoschou@github)

src/main/java/com/fasterxml/jackson/core/util/BufferRecyclers.java

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,32 @@
1515
*/
1616
public class BufferRecyclers
1717
{
18+
/**
19+
* System property that is checked to see if recycled buffers (see {@link BufferRecycler})
20+
* should be tracked, for purpose of forcing release of all such buffers, typically
21+
* during major classloading.
22+
*
23+
* @since 2.9.6
24+
*/
25+
public final static String SYSTEM_PROPERTY_TRACK_REUSABLE_BUFFERS
26+
= "com.fasterxml.jackson.core.util.BufferRecyclers.trackReusableBuffers";
27+
28+
/*
29+
/**********************************************************
30+
/* Life-cycle
31+
/**********************************************************
32+
*/
33+
34+
/**
35+
* Flag that indicates whether {@link BufferRecycler} instances should be tracked.
36+
*/
37+
private final static ThreadLocalBufferManager _bufferRecyclerTracker;
38+
static {
39+
_bufferRecyclerTracker = "true".equals(System.getProperty("com.fasterxml.jackson.core.use_releasable_thread_local_buffers"))
40+
? ThreadLocalBufferManager.instance()
41+
: null;
42+
}
43+
1844
/*
1945
/**********************************************************
2046
/* BufferRecyclers for parsers, generators
@@ -29,18 +55,46 @@ public class BufferRecyclers
2955
final protected static ThreadLocal<SoftReference<BufferRecycler>> _recyclerRef
3056
= new ThreadLocal<SoftReference<BufferRecycler>>();
3157

58+
/**
59+
* Main accessor to call for accessing possibly recycled {@link BufferRecycler} instance.
60+
*/
3261
public static BufferRecycler getBufferRecycler()
3362
{
3463
SoftReference<BufferRecycler> ref = _recyclerRef.get();
3564
BufferRecycler br = (ref == null) ? null : ref.get();
3665

3766
if (br == null) {
3867
br = new BufferRecycler();
39-
_recyclerRef.set(new SoftReference<BufferRecycler>(br));
68+
if (_bufferRecyclerTracker != null) {
69+
ref = _bufferRecyclerTracker.wrapAndTrack(br);
70+
} else {
71+
ref = new SoftReference<BufferRecycler>(br);
72+
}
73+
_recyclerRef.set(ref);
4074
}
4175
return br;
4276
}
4377

78+
/**
79+
* Specialized method that will release all recycled {@link BufferRecycler} if
80+
* (and only if) recycler tracking has been enabled
81+
* (see {@link #SYSTEM_PROPERTY_TRACK_REUSABLE_BUFFERS}).
82+
* This method is usually called on shutdown of the container like Application Server
83+
* to ensure that no references are reachable via {@link ThreadLocal}s as this may cause
84+
* unintentional retention of sizable amounts of memory. It may also be called regularly
85+
* if GC for some reason does not clear up {@link SoftReference}s aggressively enough.
86+
*
87+
* @return Number of buffers released, if tracking enabled (zero or more); -1 if tracking not enabled.
88+
*
89+
* @since 2.9.6
90+
*/
91+
public static int releaseBuffers() {
92+
if (_bufferRecyclerTracker != null) {
93+
return _bufferRecyclerTracker.releaseBuffers();
94+
}
95+
return -1;
96+
}
97+
4498
/*
4599
/**********************************************************
46100
/* JsonStringEncoder
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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+
*/
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+
*/
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+
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+
*/
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+
}

0 commit comments

Comments
 (0)