Skip to content

Commit 664cb73

Browse files
committed
ensure all buffers get released again in JsonStreamParserImpl
1 parent e00c928 commit 664cb73

File tree

3 files changed

+91
-54
lines changed

3 files changed

+91
-54
lines changed

johnzon-core/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public class JsonStreamParserImpl extends JohnzonJsonParserImpl implements JsonC
7474
//this buffer is used to store current String or Number value in case that
7575
//within the value a buffer boundary is crossed or the string contains escaped characters
7676
private char[] fallBackCopyBuffer;
77-
private boolean releaseFallBackCopyBufferLength = true;
77+
private boolean releaseFallBackCopyBuffer = true;
7878
private int fallBackCopyBufferLength;
7979
// when boundaries of fallBackCopyBuffer have been reached
8080
private List<Buffer> previousFallBackCopyBuffers;
@@ -936,6 +936,11 @@ private void combinePreviousFallbackBuffersToCurrent() {
936936
index += fallBackCopyBufferLength;
937937

938938
releasePreviousFallBackCopyBuffers();
939+
if (releaseFallBackCopyBuffer) {
940+
valueProvider.release(fallBackCopyBuffer);
941+
releaseFallBackCopyBuffer = false;
942+
}
943+
939944
fallBackCopyBuffer = newBuffer;
940945
fallBackCopyBufferLength = index;
941946
}
@@ -1038,7 +1043,7 @@ public void close() {
10381043
}
10391044

10401045
bufferProvider.release(buffer);
1041-
if (releaseFallBackCopyBufferLength) {
1046+
if (releaseFallBackCopyBuffer) {
10421047
valueProvider.release(fallBackCopyBuffer);
10431048
}
10441049
releasePreviousFallBackCopyBuffers();

johnzon-core/src/test/java/org/apache/johnzon/core/HugeStringTest.java

Lines changed: 0 additions & 52 deletions
This file was deleted.

johnzon-core/src/test/java/org/apache/johnzon/core/JsonStreamParserImplTest.java

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,26 @@
1818
*/
1919
package org.apache.johnzon.core;
2020

21+
import org.junit.Ignore;
2122
import org.junit.Test;
2223

24+
import javax.json.Json;
25+
import javax.json.JsonReader;
26+
import javax.json.JsonReaderFactory;
27+
import javax.json.spi.JsonProvider;
2328
import javax.json.stream.JsonParser;
2429
import java.io.ByteArrayInputStream;
2530
import java.io.IOException;
31+
import java.io.StringReader;
2632
import java.nio.charset.StandardCharsets;
2733
import java.util.ArrayList;
34+
import java.util.Collections;
2835
import java.util.List;
2936

3037
import static java.util.Arrays.asList;
3138
import static java.util.Collections.emptyMap;
3239
import static org.junit.Assert.assertEquals;
40+
import static org.junit.Assert.assertTrue;
3341

3442
public class JsonStreamParserImplTest {
3543
@Test
@@ -63,4 +71,80 @@ public void ensureNoArrayBoundErrorWhenOverflow() throws IOException {
6371
asList("START_OBJECT", "KEY_NAME", "VALUE_STRING", "{\"foo\":\"barbar\\barbarbar\"}", "END_OBJECT"),
6472
events);
6573
}
74+
75+
@Test
76+
@Ignore("No real test, just run directly from your IDE")
77+
public void largeStringPerformance() {
78+
StringBuilder jsonBuilder = new StringBuilder("{\"data\":\"");
79+
for (int i = 0; i < 50 * 1024 * 1024 + 1; i++) {
80+
jsonBuilder.append("a");
81+
}
82+
jsonBuilder.append("\"}");
83+
String json = jsonBuilder.toString();
84+
85+
// Warmup
86+
for (int i = 0; i < 10; i++) {
87+
try (JsonReader reader = Json.createReader(new StringReader(json))) {
88+
reader.readObject();
89+
}
90+
}
91+
92+
long start = System.currentTimeMillis();
93+
try (JsonReader reader = Json.createReader(new StringReader(json))) {
94+
reader.readObject();
95+
}
96+
System.err.println("Took " + (System.currentTimeMillis() - start) + "ms");
97+
}
98+
99+
@Test
100+
public void allBuffersReleased() {
101+
StringBuilder jsonBuilder = new StringBuilder("{\"data\":\"");
102+
for (int i = 0; i < JsonParserFactoryImpl.DEFAULT_MAX_STRING_LENGTH * 2; i++) {
103+
jsonBuilder.append("a");
104+
}
105+
jsonBuilder.append("\"}");
106+
String json = jsonBuilder.toString();
107+
108+
JsonReaderFactory readerFactory = JsonProvider.provider().createReaderFactory(Collections.singletonMap(
109+
JsonParserFactoryImpl.BUFFER_STRATEGY, TrackingBufferStrategy.class.getName()));
110+
111+
try (JsonReader reader = readerFactory.createReader(new StringReader(json))) {
112+
reader.readObject();
113+
}
114+
115+
assertTrue(TrackingBufferStrategy.TrackingBufferProvider.borrowed.isEmpty());
116+
}
117+
118+
public static class TrackingBufferStrategy implements BufferStrategy {
119+
private final BufferStrategy delegate = BufferStrategyFactory.valueOf("BY_INSTANCE");
120+
121+
@Override
122+
public BufferProvider<char[]> newCharProvider(int size) {
123+
return new TrackingBufferProvider(delegate.newCharProvider(size));
124+
}
125+
126+
public static class TrackingBufferProvider implements BufferStrategy.BufferProvider<char[]> {
127+
protected static List<char[]> borrowed = new ArrayList<>();
128+
129+
private final BufferStrategy.BufferProvider<char[]> delegate;
130+
131+
public TrackingBufferProvider(BufferStrategy.BufferProvider<char[]> delegate) {
132+
this.delegate = delegate;
133+
}
134+
135+
@Override
136+
public char[] newBuffer() {
137+
char[] result = delegate.newBuffer();
138+
borrowed.add(result);
139+
140+
return result;
141+
}
142+
143+
@Override
144+
public void release(char[] value) {
145+
borrowed.remove(value);
146+
delegate.release(value);
147+
}
148+
}
149+
}
66150
}

0 commit comments

Comments
 (0)