Skip to content

Commit a4ca94f

Browse files
authored
Introducing BufferRecyclerPool (#1064)
1 parent 0beb6fd commit a4ca94f

14 files changed

+713
-106
lines changed

src/main/java/com/fasterxml/jackson/core/JsonFactory.java

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,7 @@
1919
import com.fasterxml.jackson.core.json.async.NonBlockingJsonParser;
2020
import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer;
2121
import com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer;
22-
import com.fasterxml.jackson.core.util.BufferRecycler;
23-
import com.fasterxml.jackson.core.util.BufferRecyclerPool;
24-
import com.fasterxml.jackson.core.util.BufferRecyclers;
25-
import com.fasterxml.jackson.core.util.JacksonFeature;
26-
import com.fasterxml.jackson.core.util.JsonGeneratorDecorator;
27-
import com.fasterxml.jackson.core.util.Separators;
22+
import com.fasterxml.jackson.core.util.*;
2823

2924
/**
3025
* The main factory class of Jackson package, used to configure and
@@ -115,8 +110,10 @@ public enum Feature
115110
FAIL_ON_SYMBOL_HASH_OVERFLOW(true),
116111

117112
/**
118-
* Feature that determines whether we will use {@link BufferRecycler} with
119-
* {@link ThreadLocal} and {@link SoftReference}, for efficient reuse of
113+
* Feature that determines whether we will use a {@link BufferRecyclerPool}
114+
* for allocating and possibly recycling {@link BufferRecycler} or not.
115+
* The default {@link BufferRecyclerPool} implementation uses
116+
* {@link ThreadLocal} and {@link SoftReference} for efficient reuse of
120117
* underlying input/output buffers.
121118
* This usually makes sense on normal J2SE/J2EE server-side processing;
122119
* but may not make sense on platforms where {@link SoftReference} handling
@@ -125,6 +122,10 @@ public enum Feature
125122
* <a href="https://github.com/FasterXML/jackson-core/issues/189">jackson-core#189</a>
126123
* for a possible case)
127124
*<p>
125+
* Note that since 2.16 naming here is somewhat misleading as this is used
126+
* to now enable or disable pooling; but the actual pooling implementation
127+
* is configurable and may not be based on {@link ThreadLocal}.
128+
*<p>
128129
* This setting is enabled by default.
129130
*
130131
* @since 2.6
@@ -370,7 +371,7 @@ public static int collectDefaults() {
370371
public JsonFactory() { this((ObjectCodec) null); }
371372

372373
public JsonFactory(ObjectCodec oc) {
373-
_bufferRecyclerPool = BufferRecyclers.defaultRecyclerPool();
374+
_bufferRecyclerPool = BufferRecyclerPool.defaultPool();
374375
_objectCodec = oc;
375376
_quoteChar = DEFAULT_QUOTE_CHAR;
376377
_streamReadConstraints = StreamReadConstraints.defaults();
@@ -1873,6 +1874,7 @@ protected JsonParser _createParser(InputStream in, IOContext ctxt) throws IOExce
18731874
e.addSuppressed(e2);
18741875
}
18751876
}
1877+
ctxt.close();
18761878
throw e;
18771879
}
18781880
}
@@ -2151,7 +2153,7 @@ protected JsonGenerator _decorate(JsonGenerator g) {
21512153
*/
21522154
public BufferRecycler _getBufferRecycler()
21532155
{
2154-
return _getBufferRecyclerPool().acquireBufferRecycler(this);
2156+
return _getBufferRecyclerPool().acquireBufferRecycler();
21552157
}
21562158

21572159
/**
@@ -2165,7 +2167,7 @@ public BufferRecyclerPool _getBufferRecyclerPool() {
21652167
// scheme, for cases where it is considered harmful (possibly
21662168
// on Android, for example)
21672169
if (!Feature.USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING.enabledIn(_factoryFeatures)) {
2168-
return BufferRecyclers.nopRecyclerPool();
2170+
return BufferRecyclerPool.nonRecyclingPool();
21692171
}
21702172
return _bufferRecyclerPool;
21712173
}

src/main/java/com/fasterxml/jackson/core/TSFBuilder.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import com.fasterxml.jackson.core.json.JsonReadFeature;
1010
import com.fasterxml.jackson.core.json.JsonWriteFeature;
1111
import com.fasterxml.jackson.core.util.BufferRecyclerPool;
12-
import com.fasterxml.jackson.core.util.BufferRecyclers;
1312
import com.fasterxml.jackson.core.util.JsonGeneratorDecorator;
1413

1514
/**
@@ -142,7 +141,7 @@ protected TSFBuilder(JsonFactory base)
142141
protected TSFBuilder(int factoryFeatures,
143142
int parserFeatures, int generatorFeatures)
144143
{
145-
_bufferRecyclerPool = BufferRecyclers.defaultRecyclerPool();
144+
_bufferRecyclerPool = BufferRecyclerPool.defaultPool();
146145

147146
_factoryFeatures = factoryFeatures;
148147
_streamReadFeatures = parserFeatures;

src/main/java/com/fasterxml/jackson/core/base/ParserBase.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@ protected void _checkStdFeatureChanges(int newFeatureFlags, int changedFeatures)
395395
// as per [JACKSON-324], do in finally block
396396
// Also, internal buffer(s) can now be released as well
397397
_releaseBuffers();
398+
_ioContext.close();
398399
}
399400
}
400401
}

src/main/java/com/fasterxml/jackson/core/io/IOContext.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*<p>
1717
* NOTE: non-final since 2.4, to allow sub-classing.
1818
*/
19-
public class IOContext
19+
public class IOContext implements AutoCloseable
2020
{
2121
/*
2222
/**********************************************************************
@@ -119,6 +119,8 @@ public class IOContext
119119
*/
120120
protected char[] _nameCopyBuffer;
121121

122+
private boolean _closed = false;
123+
122124
/*
123125
/**********************************************************************
124126
/* Life-cycle
@@ -458,4 +460,12 @@ private IllegalArgumentException wrongBuf() {
458460
// sanity check failed; trying to return different, smaller buffer.
459461
return new IllegalArgumentException("Trying to release buffer smaller than original");
460462
}
463+
464+
@Override
465+
public void close() {
466+
if (!_closed) {
467+
_bufferRecycler.release();
468+
_closed = true;
469+
}
470+
}
461471
}

src/main/java/com/fasterxml/jackson/core/io/UTF8Writer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public void close()
7676
illegalSurrogate(code);
7777
}
7878
}
79+
_context.close();
7980
}
8081

8182
@Override

src/main/java/com/fasterxml/jackson/core/json/JsonGeneratorImpl.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,14 @@ public abstract class JsonGeneratorImpl extends GeneratorBase
120120
/**********************************************************
121121
*/
122122

123+
@Override
124+
public void close() throws IOException {
125+
if (!isClosed()) {
126+
super.close();
127+
_ioContext.close();
128+
}
129+
}
130+
123131
@SuppressWarnings("deprecation")
124132
public JsonGeneratorImpl(IOContext ctxt, int features, ObjectCodec codec)
125133
{
@@ -237,6 +245,23 @@ public JacksonFeatureSet<StreamWriteCapability> getWriteCapabilities() {
237245
return JSON_WRITE_CAPABILITIES;
238246
}
239247

248+
/*
249+
/**********************************************************
250+
/* Misc other accessors
251+
/**********************************************************
252+
*/
253+
254+
/**
255+
* Accessor for use by {@code jackson-core} itself (tests in particular).
256+
*
257+
* @return {@link IOContext} in use by this generator
258+
*
259+
* @since 2.16
260+
*/
261+
public IOContext ioContext() {
262+
return _ioContext;
263+
}
264+
240265
/*
241266
/**********************************************************
242267
/* Shared helper methods

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

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
package com.fasterxml.jackson.core.util;
22

3+
import java.util.Objects;
34
import java.util.concurrent.atomic.AtomicReferenceArray;
45

56
/**
67
* This is a small utility class, whose main functionality is to allow
7-
* simple reuse of raw byte/char buffers. It is usually used through
8-
* <code>ThreadLocal</code> member of the owning class pointing to
9-
* instance of this class through a <code>SoftReference</code>. The
10-
* end result is a low-overhead GC-cleanable recycling: hopefully
8+
* simple reuse of raw byte/char buffers. It is usually allocated through
9+
* {@link BufferRecyclerPool} (starting with 2.16): multiple pool
10+
* implementations exists.
11+
* The default pool implementation uses
12+
* {@code ThreadLocal} combined with {@code SoftReference}.
13+
* The end result is a low-overhead GC-cleanable recycling: hopefully
1114
* ideal for use by stream readers.
1215
*<p>
1316
* Rewritten in 2.10 to be thread-safe (see [jackson-core#479] for details),
14-
* to not rely on {@code ThreadLocal} access.
17+
* to not rely on {@code ThreadLocal} access.<br />
18+
* Rewritten in 2.16 to work with {@link BufferRecyclerPool} abstraction.
1519
*/
1620
public class BufferRecycler
1721
{
@@ -82,6 +86,8 @@ public class BufferRecycler
8286
// Note: changed from simple array in 2.10:
8387
protected final AtomicReferenceArray<char[]> _charBuffers;
8488

89+
private BufferRecyclerPool _pool;
90+
8591
/*
8692
/**********************************************************
8793
/* Construction
@@ -189,4 +195,36 @@ protected int charBufferLength(int ix) {
189195

190196
protected byte[] balloc(int size) { return new byte[size]; }
191197
protected char[] calloc(int size) { return new char[size]; }
198+
199+
/**
200+
* Method called by owner of this recycler instance, to provide reference to
201+
* {@link BufferRecyclerPool} into which instance is to be released (if any)
202+
*
203+
* @since 2.16
204+
*/
205+
BufferRecycler withPool(BufferRecyclerPool pool) {
206+
if (this._pool != null) {
207+
throw new IllegalStateException("BufferRecycler already linked to pool: "+pool);
208+
}
209+
// assign to pool to which this BufferRecycler belongs in order to release it
210+
// to the same pool when the work will be completed
211+
_pool = Objects.requireNonNull(pool);
212+
return this;
213+
}
214+
215+
/**
216+
* Method called when owner of this recycler no longer wishes use it; this should
217+
* return it to pool passed via {@code withPool()} (if any).
218+
*
219+
* @since 2.16
220+
*/
221+
public void release() {
222+
if (_pool != null) {
223+
BufferRecyclerPool tmpPool = _pool;
224+
// nullify the reference to the pool in order to avoid the risk of releasing
225+
// the same BufferRecycler more than once, thus compromising the pool integrity
226+
_pool = null;
227+
tmpPool.releaseBufferRecycler(this);
228+
}
229+
}
192230
}

0 commit comments

Comments
 (0)