Skip to content
This repository has been archived by the owner on Dec 23, 2023. It is now read-only.

Avoid using finalizers when not leaking open spans #2046

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -1,5 +1,5 @@
/*
* Copyright 2017, OpenCensus Authors
* Copyright 2017-20, OpenCensus Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -43,6 +43,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
Expand All @@ -52,12 +53,15 @@
// TODO(hailongwen): remove the usage of `NetworkEvent` in the future.
/** Implementation for the {@link Span} class that records trace events. */
@ThreadSafe
public final class RecordEventsSpanImpl extends Span implements Element<RecordEventsSpanImpl> {
private static final Logger logger = Logger.getLogger(Tracer.class.getName());
public class RecordEventsSpanImpl extends Span implements Element<RecordEventsSpanImpl> {
protected static final Logger logger = Logger.getLogger(Tracer.class.getName());

private static final EnumSet<Span.Options> RECORD_EVENTS_SPAN_OPTIONS =
EnumSet.of(Span.Options.RECORD_EVENTS);

private static final AtomicInteger CURRENT_OPEN_SPANS = new AtomicInteger(0);
private static final AtomicInteger MAX_SEEN_OPEN_SPANS = new AtomicInteger(0);

// The parent SpanId of this span. Null if this is a root span.
@Nullable private final SpanId parentSpanId;
// True if the parent is on a different process.
Expand All @@ -67,7 +71,7 @@ public final class RecordEventsSpanImpl extends Span implements Element<RecordEv
// Handler called when the span starts and ends.
private final StartEndHandler startEndHandler;
// The displayed name of the span.
private final String name;
protected final String name;
// The kind of the span.
@Nullable private final Kind kind;
// The clock used to get the time.
Expand Down Expand Up @@ -105,7 +109,7 @@ public final class RecordEventsSpanImpl extends Span implements Element<RecordEv
private long endNanoTime;
// True if the span is ended.
@GuardedBy("this")
private boolean hasBeenEnded;
protected boolean hasBeenEnded;

@GuardedBy("this")
private boolean sampleToLocalSpanStore;
Expand Down Expand Up @@ -141,23 +145,52 @@ public static RecordEventsSpanImpl startSpan(
StartEndHandler startEndHandler,
@Nullable TimestampConverter timestampConverter,
Clock clock) {
RecordEventsSpanImpl span =
new RecordEventsSpanImpl(
context,
name,
kind,
parentSpanId,
hasRemoteParent,
traceParams,
startEndHandler,
timestampConverter,
clock);
RecordEventsSpanImpl span;

if (isLeakingSpans()) {
span =
new RecordEventsSpanImplWithFinalizer(
context,
name,
kind,
parentSpanId,
hasRemoteParent,
traceParams,
startEndHandler,
timestampConverter,
clock);
} else {
span =
new RecordEventsSpanImpl(
context,
name,
kind,
parentSpanId,
hasRemoteParent,
traceParams,
startEndHandler,
timestampConverter,
clock);
}
// Call onStart here instead of calling in the constructor to make sure the span is completely
// initialized.
startEndHandler.onStart(span);
return span;
}

private static boolean isLeakingSpans() {
final int current = CURRENT_OPEN_SPANS.get();
while (true) {
final int max = MAX_SEEN_OPEN_SPANS.get();
if (current < max) {
return false;
}
if (MAX_SEEN_OPEN_SPANS.compareAndSet(max, current)) {
return true;
}
}
}

/**
* Returns the name of the {@code Span}.
*
Expand Down Expand Up @@ -379,6 +412,7 @@ public void end(EndSpanOptions options) {
}
sampleToLocalSpanStore = options.getSampleToLocalSpanStore();
endNanoTime = clock.nowNanos();
CURRENT_OPEN_SPANS.decrementAndGet();
hasBeenEnded = true;
}
startEndHandler.onEnd(this);
Expand Down Expand Up @@ -561,7 +595,7 @@ private TimedEvent<T> toSpanDataTimedEvent(TimestampConverter timestampConverter
}
}

private RecordEventsSpanImpl(
protected RecordEventsSpanImpl(
SpanContext context,
String name,
@Nullable Kind kind,
Expand All @@ -580,21 +614,11 @@ private RecordEventsSpanImpl(
this.startEndHandler = startEndHandler;
this.clock = clock;
this.hasBeenEnded = false;
CURRENT_OPEN_SPANS.incrementAndGet();
this.sampleToLocalSpanStore = false;
this.numberOfChildren = 0;
this.timestampConverter =
timestampConverter != null ? timestampConverter : TimestampConverter.now(clock);
startNanoTime = clock.nowNanos();
}

@SuppressWarnings("NoFinalizer")
@Override
protected void finalize() throws Throwable {
synchronized (this) {
if (!hasBeenEnded) {
logger.log(Level.SEVERE, "Span " + name + " is GC'ed without being ended.");
}
}
super.finalize();
}
}
@@ -0,0 +1,61 @@
/*
* Copyright 2020, OpenCensus Authors
*
* Licensed 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 io.opencensus.implcore.trace;

import io.opencensus.common.Clock;
import io.opencensus.implcore.internal.TimestampConverter;
import io.opencensus.trace.SpanContext;
import io.opencensus.trace.SpanId;
import io.opencensus.trace.config.TraceParams;
import java.util.logging.Level;
import javax.annotation.Nullable;

public final class RecordEventsSpanImplWithFinalizer extends RecordEventsSpanImpl {

protected RecordEventsSpanImplWithFinalizer(
SpanContext context,
String name,
@Nullable Kind kind,
@Nullable SpanId parentSpanId,
@Nullable Boolean hasRemoteParent,
TraceParams traceParams,
StartEndHandler startEndHandler,
@Nullable TimestampConverter timestampConverter,
Clock clock) {
super(
context,
name,
kind,
parentSpanId,
hasRemoteParent,
traceParams,
startEndHandler,
timestampConverter,
clock);
}

@SuppressWarnings("NoFinalizer")
@Override
protected void finalize() throws Throwable {
synchronized (this) {
if (!hasBeenEnded) {
logger.log(Level.SEVERE, "Span " + name + " is GC'ed without being ended.");
}
}
super.finalize();
}
}