Skip to content

Commit 54c3d1a

Browse files
committed
Remove SoftReferences from StreamInput/StreamOutput
We try to reuse character arrays and UTF8 writers with softreferences. SoftReferences have negative impact on GC and should be avoided in general. Yet in this case it can simply replaced with a per-stream Bytes/CharsRef that is thread local and has the same lifetime as the stream.
1 parent 341f54d commit 54c3d1a

File tree

2 files changed

+21
-48
lines changed

2 files changed

+21
-48
lines changed

src/main/java/org/elasticsearch/common/io/stream/StreamInput.java

+13-23
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@
1919

2020
package org.elasticsearch.common.io.stream;
2121

22-
import org.apache.lucene.util.ArrayUtil;
2322
import org.apache.lucene.util.BytesRef;
24-
import org.apache.lucene.util.RamUsageEstimator;
23+
import org.apache.lucene.util.CharsRef;
2524
import org.elasticsearch.Version;
2625
import org.elasticsearch.common.Nullable;
2726
import org.elasticsearch.common.Strings;
@@ -34,26 +33,13 @@
3433

3534
import java.io.IOException;
3635
import java.io.InputStream;
37-
import java.lang.ref.SoftReference;
3836
import java.util.*;
3937

4038
/**
4139
*
4240
*/
4341
public abstract class StreamInput extends InputStream {
4442

45-
private static final ThreadLocal<SoftReference<char[]>> charCache = new ThreadLocal<>();
46-
47-
private static char[] charCache(int size) {
48-
SoftReference<char[]> ref = charCache.get();
49-
char[] arr = (ref == null) ? null : ref.get();
50-
if (arr == null || arr.length < size) {
51-
arr = new char[ArrayUtil.oversize(size, RamUsageEstimator.NUM_BYTES_CHAR)];
52-
charCache.set(new SoftReference<>(arr));
53-
}
54-
return arr;
55-
}
56-
5743
private Version version = Version.CURRENT;
5844

5945
public Version getVersion() {
@@ -268,11 +254,15 @@ public String readOptionalSharedString() throws IOException {
268254
return null;
269255
}
270256

257+
private final CharsRef spare = new CharsRef();
258+
271259
public String readString() throws IOException {
272-
int charCount = readVInt();
273-
char[] chars = charCache(charCount);
274-
int c, charIndex = 0;
275-
while (charIndex < charCount) {
260+
final int charCount = readVInt();
261+
spare.offset = 0;
262+
spare.length = 0;
263+
spare.grow(charCount);
264+
int c = 0;
265+
while (spare.length < charCount) {
276266
c = readByte() & 0xff;
277267
switch (c >> 4) {
278268
case 0:
@@ -283,18 +273,18 @@ public String readString() throws IOException {
283273
case 5:
284274
case 6:
285275
case 7:
286-
chars[charIndex++] = (char) c;
276+
spare.chars[spare.length++] = (char) c;
287277
break;
288278
case 12:
289279
case 13:
290-
chars[charIndex++] = (char) ((c & 0x1F) << 6 | readByte() & 0x3F);
280+
spare.chars[spare.length++] = (char) ((c & 0x1F) << 6 | readByte() & 0x3F);
291281
break;
292282
case 14:
293-
chars[charIndex++] = (char) ((c & 0x0F) << 12 | (readByte() & 0x3F) << 6 | (readByte() & 0x3F) << 0);
283+
spare.chars[spare.length++] = (char) ((c & 0x0F) << 12 | (readByte() & 0x3F) << 6 | (readByte() & 0x3F) << 0);
294284
break;
295285
}
296286
}
297-
return new String(chars, 0, charCount);
287+
return spare.toString();
298288
}
299289

300290
public String readSharedString() throws IOException {

src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java

+8-25
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package org.elasticsearch.common.io.stream;
2121

2222
import org.apache.lucene.util.BytesRef;
23+
import org.apache.lucene.util.UnicodeUtil;
2324
import org.elasticsearch.Version;
2425
import org.elasticsearch.common.Nullable;
2526
import org.elasticsearch.common.bytes.BytesReference;
@@ -40,19 +41,6 @@
4041
*/
4142
public abstract class StreamOutput extends OutputStream {
4243

43-
private static ThreadLocal<SoftReference<UTF8StreamWriter>> utf8StreamWriter = new ThreadLocal<>();
44-
45-
public static UTF8StreamWriter utf8StreamWriter() {
46-
SoftReference<UTF8StreamWriter> ref = utf8StreamWriter.get();
47-
UTF8StreamWriter writer = (ref == null) ? null : ref.get();
48-
if (writer == null) {
49-
writer = new UTF8StreamWriter(1024 * 4);
50-
utf8StreamWriter.set(new SoftReference<>(writer));
51-
}
52-
writer.reset();
53-
return writer;
54-
}
55-
5644
private Version version = Version.CURRENT;
5745

5846
public Version getVersion() {
@@ -207,19 +195,14 @@ public void writeOptionalText(@Nullable Text text) throws IOException {
207195
}
208196
}
209197

198+
private final BytesRef spare = new BytesRef();
199+
210200
public void writeText(Text text) throws IOException {
211-
if (!text.hasBytes() && seekPositionSupported()) {
212-
long pos1 = position();
213-
// make room for the size
214-
seek(pos1 + 4);
215-
UTF8StreamWriter utf8StreamWriter = utf8StreamWriter();
216-
utf8StreamWriter.setOutput(this);
217-
utf8StreamWriter.write(text.string());
218-
utf8StreamWriter.close();
219-
long pos2 = position();
220-
seek(pos1);
221-
writeInt((int) (pos2 - pos1 - 4));
222-
seek(pos2);
201+
if (!text.hasBytes()) {
202+
final String string = text.string();
203+
UnicodeUtil.UTF16toUTF8(string, 0, string.length(), spare);
204+
writeInt(spare.length);
205+
write(spare.bytes, spare.offset, spare.length);
223206
} else {
224207
BytesReference bytes = text.bytes();
225208
writeInt(bytes.length());

0 commit comments

Comments
 (0)