This is an automated email from the ASF dual-hosted git repository. jungm pushed a commit to branch johnzon-1.2.x in repository https://gitbox.apache.org/repos/asf/johnzon.git
The following commit(s) were added to refs/heads/johnzon-1.2.x by this push: new 664cb73f ensure all buffers get released again in JsonStreamParserImpl 664cb73f is described below commit 664cb73f629fd0190902a0f1080c420a509e66b0 Author: Markus Jung <ju...@apache.org> AuthorDate: Tue Feb 13 15:33:06 2024 +0100 ensure all buffers get released again in JsonStreamParserImpl --- .../apache/johnzon/core/JsonStreamParserImpl.java | 9 ++- .../org/apache/johnzon/core/HugeStringTest.java | 52 -------------- .../johnzon/core/JsonStreamParserImplTest.java | 84 ++++++++++++++++++++++ 3 files changed, 91 insertions(+), 54 deletions(-) diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java index a3cfde66..a1ef78a6 100644 --- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java +++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java @@ -74,7 +74,7 @@ public class JsonStreamParserImpl extends JohnzonJsonParserImpl implements JsonC //this buffer is used to store current String or Number value in case that //within the value a buffer boundary is crossed or the string contains escaped characters private char[] fallBackCopyBuffer; - private boolean releaseFallBackCopyBufferLength = true; + private boolean releaseFallBackCopyBuffer = true; private int fallBackCopyBufferLength; // when boundaries of fallBackCopyBuffer have been reached private List<Buffer> previousFallBackCopyBuffers; @@ -936,6 +936,11 @@ public class JsonStreamParserImpl extends JohnzonJsonParserImpl implements JsonC index += fallBackCopyBufferLength; releasePreviousFallBackCopyBuffers(); + if (releaseFallBackCopyBuffer) { + valueProvider.release(fallBackCopyBuffer); + releaseFallBackCopyBuffer = false; + } + fallBackCopyBuffer = newBuffer; fallBackCopyBufferLength = index; } @@ -1038,7 +1043,7 @@ public class JsonStreamParserImpl extends JohnzonJsonParserImpl implements JsonC } bufferProvider.release(buffer); - if (releaseFallBackCopyBufferLength) { + if (releaseFallBackCopyBuffer) { valueProvider.release(fallBackCopyBuffer); } releasePreviousFallBackCopyBuffers(); diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/HugeStringTest.java b/johnzon-core/src/test/java/org/apache/johnzon/core/HugeStringTest.java deleted file mode 100644 index 5e0bf8b0..00000000 --- a/johnzon-core/src/test/java/org/apache/johnzon/core/HugeStringTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.johnzon.core; - -import org.junit.Ignore; -import org.junit.Test; - -import javax.json.Json; -import javax.json.JsonReader; -import java.io.StringReader; - -@Ignore -public class HugeStringTest { - @Test - public void test() { - StringBuilder jsonBuilder = new StringBuilder("{\"data\":\""); - for (int i = 0; i < 50 * 1024 * 1024 + 1; i++) { - jsonBuilder.append("a"); - } - jsonBuilder.append("\"}"); - String json = jsonBuilder.toString(); - - // Warmup - for (int i = 0; i < 10; i++) { - try (JsonReader reader = Json.createReader(new StringReader(json))) { - reader.readObject(); - } - } - - long start = System.currentTimeMillis(); - try (JsonReader reader = Json.createReader(new StringReader(json))) { - reader.readObject(); - } - System.err.println("Took " + (System.currentTimeMillis() - start) + "ms"); - } -} diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonStreamParserImplTest.java b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonStreamParserImplTest.java index f155d138..ef3e03e5 100644 --- a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonStreamParserImplTest.java +++ b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonStreamParserImplTest.java @@ -18,18 +18,26 @@ */ package org.apache.johnzon.core; +import org.junit.Ignore; import org.junit.Test; +import javax.json.Json; +import javax.json.JsonReader; +import javax.json.JsonReaderFactory; +import javax.json.spi.JsonProvider; import javax.json.stream.JsonParser; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.StringReader; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import static java.util.Arrays.asList; import static java.util.Collections.emptyMap; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class JsonStreamParserImplTest { @Test @@ -63,4 +71,80 @@ public class JsonStreamParserImplTest { asList("START_OBJECT", "KEY_NAME", "VALUE_STRING", "{\"foo\":\"barbar\\barbarbar\"}", "END_OBJECT"), events); } + + @Test + @Ignore("No real test, just run directly from your IDE") + public void largeStringPerformance() { + StringBuilder jsonBuilder = new StringBuilder("{\"data\":\""); + for (int i = 0; i < 50 * 1024 * 1024 + 1; i++) { + jsonBuilder.append("a"); + } + jsonBuilder.append("\"}"); + String json = jsonBuilder.toString(); + + // Warmup + for (int i = 0; i < 10; i++) { + try (JsonReader reader = Json.createReader(new StringReader(json))) { + reader.readObject(); + } + } + + long start = System.currentTimeMillis(); + try (JsonReader reader = Json.createReader(new StringReader(json))) { + reader.readObject(); + } + System.err.println("Took " + (System.currentTimeMillis() - start) + "ms"); + } + + @Test + public void allBuffersReleased() { + StringBuilder jsonBuilder = new StringBuilder("{\"data\":\""); + for (int i = 0; i < JsonParserFactoryImpl.DEFAULT_MAX_STRING_LENGTH * 2; i++) { + jsonBuilder.append("a"); + } + jsonBuilder.append("\"}"); + String json = jsonBuilder.toString(); + + JsonReaderFactory readerFactory = JsonProvider.provider().createReaderFactory(Collections.singletonMap( + JsonParserFactoryImpl.BUFFER_STRATEGY, TrackingBufferStrategy.class.getName())); + + try (JsonReader reader = readerFactory.createReader(new StringReader(json))) { + reader.readObject(); + } + + assertTrue(TrackingBufferStrategy.TrackingBufferProvider.borrowed.isEmpty()); + } + + public static class TrackingBufferStrategy implements BufferStrategy { + private final BufferStrategy delegate = BufferStrategyFactory.valueOf("BY_INSTANCE"); + + @Override + public BufferProvider<char[]> newCharProvider(int size) { + return new TrackingBufferProvider(delegate.newCharProvider(size)); + } + + public static class TrackingBufferProvider implements BufferStrategy.BufferProvider<char[]> { + protected static List<char[]> borrowed = new ArrayList<>(); + + private final BufferStrategy.BufferProvider<char[]> delegate; + + public TrackingBufferProvider(BufferStrategy.BufferProvider<char[]> delegate) { + this.delegate = delegate; + } + + @Override + public char[] newBuffer() { + char[] result = delegate.newBuffer(); + borrowed.add(result); + + return result; + } + + @Override + public void release(char[] value) { + borrowed.remove(value); + delegate.release(value); + } + } + } }