diff --git a/CHANGELOG.md b/CHANGELOG.md index 73bb2ef396..750422b38c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - Probe class availability without initializing the class during SDK init ([#5635](https://github.com/getsentry/sentry-java/pull/5635)) - Avoid constructing an exception per view when resolving view ids during view-hierarchy and gesture capture ([#5631](https://github.com/getsentry/sentry-java/pull/5631)) - Start the frame metrics thread lazily on first collection instead of during SDK init ([#5641](https://github.com/getsentry/sentry-java/pull/5641)) +- Reduce `SentryId` and `SpanId` allocation overhead by replacing their per-instance `LazyEvaluator` (and its lock) with a lightweight lazily-generated `String`. ([#5645](https://github.com/getsentry/sentry-java/pull/5645)) ## 8.46.0 diff --git a/sentry/src/main/java/io/sentry/SpanId.java b/sentry/src/main/java/io/sentry/SpanId.java index fcc7f3a4f3..2048647f9f 100644 --- a/sentry/src/main/java/io/sentry/SpanId.java +++ b/sentry/src/main/java/io/sentry/SpanId.java @@ -2,24 +2,35 @@ import static io.sentry.util.StringUtils.PROPER_NIL_UUID; -import io.sentry.util.LazyEvaluator; import java.io.IOException; import java.util.Objects; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public final class SpanId implements JsonSerializable { public static final SpanId EMPTY_ID = new SpanId(PROPER_NIL_UUID.replace("-", "").substring(0, 16)); - private final @NotNull LazyEvaluator lazyValue; + private volatile @Nullable String value; public SpanId(final @NotNull String value) { - Objects.requireNonNull(value, "value is required"); - this.lazyValue = new LazyEvaluator<>(() -> value); + this.value = Objects.requireNonNull(value, "value is required"); } - public SpanId() { - this.lazyValue = new LazyEvaluator<>(SentryUUID::generateSpanId); + public SpanId() {} + + private @NotNull String getValue() { + String result = value; + if (result == null) { + synchronized (this) { + result = value; + if (result == null) { + result = SentryUUID.generateSpanId(); + value = result; + } + } + } + return result; } @Override @@ -27,17 +38,17 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SpanId spanId = (SpanId) o; - return lazyValue.getValue().equals(spanId.lazyValue.getValue()); + return getValue().equals(spanId.getValue()); } @Override public int hashCode() { - return lazyValue.getValue().hashCode(); + return getValue().hashCode(); } @Override public String toString() { - return lazyValue.getValue(); + return getValue(); } // JsonElementSerializer @@ -45,7 +56,7 @@ public String toString() { @Override public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger logger) throws IOException { - writer.value(lazyValue.getValue()); + writer.value(getValue()); } // JsonElementDeserializer diff --git a/sentry/src/main/java/io/sentry/protocol/SentryId.java b/sentry/src/main/java/io/sentry/protocol/SentryId.java index a5bd7980c3..8d85afe463 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryId.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryId.java @@ -6,7 +6,6 @@ import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.SentryUUID; -import io.sentry.util.LazyEvaluator; import io.sentry.util.StringUtils; import io.sentry.util.UUIDStringUtils; import java.io.IOException; @@ -19,19 +18,15 @@ public final class SentryId implements JsonSerializable { public static final SentryId EMPTY_ID = new SentryId(StringUtils.PROPER_NIL_UUID.replace("-", "")); - private final @NotNull LazyEvaluator lazyStringValue; + private volatile @Nullable String value; + private final @Nullable UUID uuid; public SentryId() { this((UUID) null); } public SentryId(@Nullable UUID uuid) { - if (uuid != null) { - this.lazyStringValue = - new LazyEvaluator<>(() -> normalize(UUIDStringUtils.toSentryIdString(uuid))); - } else { - this.lazyStringValue = new LazyEvaluator<>(SentryUUID::generateSentryId); - } + this.uuid = uuid; } public SentryId(final @NotNull String sentryIdString) { @@ -42,16 +37,30 @@ public SentryId(final @NotNull String sentryIdString) { + "or 36 characters long (completed UUID). Received: " + sentryIdString); } - if (normalized.length() == 36) { - this.lazyStringValue = new LazyEvaluator<>(() -> normalize(normalized)); - } else { - this.lazyStringValue = new LazyEvaluator<>(() -> normalized); + this.uuid = null; + this.value = normalized.length() == 36 ? normalized.replace("-", "") : normalized; + } + + private @NotNull String getValue() { + String result = value; + if (result == null) { + synchronized (this) { + result = value; + if (result == null) { + result = + uuid != null + ? normalize(UUIDStringUtils.toSentryIdString(uuid)) + : SentryUUID.generateSentryId(); + value = result; + } + } } + return result; } @Override public String toString() { - return lazyStringValue.getValue(); + return getValue(); } @Override @@ -59,12 +68,12 @@ public boolean equals(final @Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SentryId sentryId = (SentryId) o; - return lazyStringValue.getValue().equals(sentryId.lazyStringValue.getValue()); + return getValue().equals(sentryId.getValue()); } @Override public int hashCode() { - return lazyStringValue.getValue().hashCode(); + return getValue().hashCode(); } private @NotNull String normalize(@NotNull String uuidString) {