From 2382736e28ee70ee8d9bea96f1c42146b9b272b6 Mon Sep 17 00:00:00 2001 From: joerg1985 <16140691+joerg1985@users.noreply.github.com> Date: Mon, 10 Jul 2023 09:54:33 +0200 Subject: [PATCH] [java] less memory allocation (#12320) --- .../netty/server/ResponseConverter.java | 7 ++--- .../remote/codec/w3c/W3CHttpCommandCodec.java | 27 +++++++++++++------ .../selenium/remote/tracing/HttpTracing.java | 12 +++++---- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/java/src/org/openqa/selenium/netty/server/ResponseConverter.java b/java/src/org/openqa/selenium/netty/server/ResponseConverter.java index 305946de87242..6e56c0304f593 100644 --- a/java/src/org/openqa/selenium/netty/server/ResponseConverter.java +++ b/java/src/org/openqa/selenium/netty/server/ResponseConverter.java @@ -40,6 +40,7 @@ public class ResponseConverter extends ChannelOutboundHandlerAdapter { private static final int CHUNK_SIZE = 1024 * 1024; + private static final ThreadLocal CHUNK_CACHE = ThreadLocal.withInitial(() -> new byte[CHUNK_SIZE]); private final boolean allowCors; public ResponseConverter(boolean allowCors) { @@ -57,7 +58,7 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) HttpResponse seResponse = (HttpResponse) msg; // We may not know how large the response is, but figure it out if we can. - byte[] ary = new byte[CHUNK_SIZE]; + byte[] ary = CHUNK_CACHE.get(); InputStream is = seResponse.getContent().get(); int byteCount = ByteStreams.read(is, ary, 0, ary.length); // If there are no bytes left to read, then -1 is returned by read, and this is bad. @@ -70,7 +71,7 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) new DefaultFullHttpResponse( HTTP_1_1, HttpResponseStatus.valueOf(seResponse.getStatus()), - Unpooled.wrappedBuffer(ary, 0, byteCount)); + Unpooled.copiedBuffer(ary, 0, byteCount)); first.headers().addInt(CONTENT_LENGTH, byteCount); copyHeaders(seResponse, first); ctx.write(first); @@ -81,7 +82,7 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) ctx.write(first); // We need to write the first response. - ctx.write(new DefaultHttpContent(Unpooled.wrappedBuffer(ary))); + ctx.write(new DefaultHttpContent(Unpooled.copiedBuffer(ary))); HttpChunkedInput writer = new HttpChunkedInput(new ChunkedStream(is, CHUNK_SIZE)); ChannelFuture future = ctx.write(writer); diff --git a/java/src/org/openqa/selenium/remote/codec/w3c/W3CHttpCommandCodec.java b/java/src/org/openqa/selenium/remote/codec/w3c/W3CHttpCommandCodec.java index 7a5b6c1cc272f..7550044b489ed 100644 --- a/java/src/org/openqa/selenium/remote/codec/w3c/W3CHttpCommandCodec.java +++ b/java/src/org/openqa/selenium/remote/codec/w3c/W3CHttpCommandCodec.java @@ -75,6 +75,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.io.Resources; import java.io.IOException; +import java.io.UncheckedIOException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -82,6 +83,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import java.util.stream.Stream; import org.openqa.selenium.InvalidSelectorException; @@ -96,6 +98,8 @@ */ public class W3CHttpCommandCodec extends AbstractHttpCommandCodec { + private static final ConcurrentHashMap ATOM_SCRIPTS = new ConcurrentHashMap<>(); + public W3CHttpCommandCodec() { String sessionId = "/session/:sessionId"; @@ -338,15 +342,22 @@ private List stringToUtf8Array(String toConvert) { private Map executeAtom(String atomFileName, Object... args) { try { - String scriptName = "/org/openqa/selenium/remote/" + atomFileName; - URL url = getClass().getResource(scriptName); - - String rawFunction = Resources.toString(url, StandardCharsets.UTF_8); - String atomName = atomFileName.replace(".js", ""); - String script = - String.format("/* %s */return (%s).apply(null, arguments);", atomName, rawFunction); + String script = ATOM_SCRIPTS.computeIfAbsent(atomFileName, (fileName) -> { + String scriptName = "/org/openqa/selenium/remote/" + atomFileName; + URL url = getClass().getResource(scriptName); + String rawFunction; + try { + rawFunction = Resources.toString(url, StandardCharsets.UTF_8); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + String atomName = fileName.replace(".js", ""); + return String.format("/* %s */return (%s).apply(null, arguments);", atomName, rawFunction); + }); return toScript(script, args); - } catch (IOException | NullPointerException e) { + } catch (UncheckedIOException e) { + throw new WebDriverException(e.getCause()); + } catch (NullPointerException e) { throw new WebDriverException(e); } } diff --git a/java/src/org/openqa/selenium/remote/tracing/HttpTracing.java b/java/src/org/openqa/selenium/remote/tracing/HttpTracing.java index c3a1c0867f8b0..a072e26356920 100644 --- a/java/src/org/openqa/selenium/remote/tracing/HttpTracing.java +++ b/java/src/org/openqa/selenium/remote/tracing/HttpTracing.java @@ -17,6 +17,7 @@ package org.openqa.selenium.remote.tracing; +import java.util.logging.Level; import java.util.logging.Logger; import org.openqa.selenium.internal.Require; import org.openqa.selenium.remote.http.HttpRequest; @@ -56,11 +57,12 @@ public static void inject(Tracer tracer, TraceContext context, HttpRequest reque Require.nonNull("Tracer", tracer); Require.nonNull("Request", request); - StackTraceElement caller = Thread.currentThread().getStackTrace()[2]; - LOG.fine( - String.format( - "Injecting %s into %s at %s:%d", - request, context, caller.getClassName(), caller.getLineNumber())); + if (LOG.isLoggable(Level.FINE)) { + StackTraceElement caller = Thread.currentThread().getStackTrace()[2]; + LOG.log( + Level.FINE, "Injecting {0} into {1} at {2}:{3}", + new Object[] {request, context, caller.getClassName(), caller.getLineNumber()}); + } tracer.getPropagator().inject(context, request, (req, key, value) -> req.setHeader(key, value)); }