From 3b13f34f249b518de31a9032dc17666205042c7b Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 23 Jun 2026 06:40:36 +0200 Subject: [PATCH] perf(core): Reduce context serialization allocations Use sorted key arrays when serializing contexts to avoid allocating an ArrayList for each serialization. This preserves deterministic key ordering while keeping the snapshot representation smaller. Co-Authored-By: Claude --- .../main/java/io/sentry/MonitorContexts.java | 8 ++++---- .../java/io/sentry/protocol/Contexts.java | 8 ++++---- .../java/io/sentry/MonitorContextsTest.kt | 19 +++++++++++++++++++ 3 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 sentry/src/test/java/io/sentry/MonitorContextsTest.kt diff --git a/sentry/src/main/java/io/sentry/MonitorContexts.java b/sentry/src/main/java/io/sentry/MonitorContexts.java index 193d9ee5a6f..af8a963f63a 100644 --- a/sentry/src/main/java/io/sentry/MonitorContexts.java +++ b/sentry/src/main/java/io/sentry/MonitorContexts.java @@ -3,8 +3,7 @@ import io.sentry.util.Objects; import io.sentry.vendor.gson.stream.JsonToken; import java.io.IOException; -import java.util.Collections; -import java.util.List; +import java.util.Arrays; import java.util.concurrent.ConcurrentHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -12,6 +11,7 @@ public final class MonitorContexts extends ConcurrentHashMap implements JsonSerializable { private static final long serialVersionUID = 3987329379811822556L; + private static final String[] EMPTY_KEYS = new String[0]; public MonitorContexts() {} @@ -49,8 +49,8 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger throws IOException { writer.beginObject(); // Serialize in alphabetical order to keep determinism. - final List sortedKeys = Collections.list(keys()); - Collections.sort(sortedKeys); + final String[] sortedKeys = keySet().toArray(EMPTY_KEYS); + Arrays.sort(sortedKeys); for (final String key : sortedKeys) { final Object value = get(key); if (value != null) { diff --git a/sentry/src/main/java/io/sentry/protocol/Contexts.java b/sentry/src/main/java/io/sentry/protocol/Contexts.java index fd1e9b83eb6..82f70bb712f 100644 --- a/sentry/src/main/java/io/sentry/protocol/Contexts.java +++ b/sentry/src/main/java/io/sentry/protocol/Contexts.java @@ -14,10 +14,9 @@ import io.sentry.util.Objects; import io.sentry.vendor.gson.stream.JsonToken; import java.io.IOException; -import java.util.Collections; +import java.util.Arrays; import java.util.Enumeration; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -28,6 +27,7 @@ public class Contexts implements JsonSerializable { private static final long serialVersionUID = 252445813254943011L; public static final String REPLAY_ID = "replay_id"; + private static final String[] EMPTY_KEYS = new String[0]; private final @NotNull ConcurrentHashMap internalStorage = new ConcurrentHashMap<>(); @@ -302,8 +302,8 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger throws IOException { writer.beginObject(); // Serialize in alphabetical order to keep determinism. - final List sortedKeys = Collections.list(keys()); - Collections.sort(sortedKeys); + final String[] sortedKeys = internalStorage.keySet().toArray(EMPTY_KEYS); + Arrays.sort(sortedKeys); for (final String key : sortedKeys) { final Object value = get(key); if (value != null) { diff --git a/sentry/src/test/java/io/sentry/MonitorContextsTest.kt b/sentry/src/test/java/io/sentry/MonitorContextsTest.kt new file mode 100644 index 00000000000..2b0d57e605e --- /dev/null +++ b/sentry/src/test/java/io/sentry/MonitorContextsTest.kt @@ -0,0 +1,19 @@ +package io.sentry + +import io.sentry.protocol.SerializationUtils +import kotlin.test.Test +import kotlin.test.assertEquals +import org.mockito.kotlin.mock + +class MonitorContextsTest { + @Test + fun `serializes entries in alphabetical order`() { + val contexts = + MonitorContexts().apply { + put("b", 2) + put("a", 1) + } + + assertEquals("{\"a\":1,\"b\":2}", SerializationUtils.serializeToString(contexts, mock())) + } +}