Merge pull request #619 from DataDog/mar-kolya/fix-memory-leak-in-weak-map

Mar kolya/fix memory leak in weak map
This commit is contained in:
Nikolay Martynov 2018-12-05 17:32:42 -05:00 committed by GitHub
commit 44aadf36ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 29 additions and 12 deletions

View File

@ -54,7 +54,7 @@ class WeakMapSuppliers {
}; };
private final ScheduledExecutorService cleanerExecutorService; private final ScheduledExecutorService cleanerExecutorService;
private final Thread shutdownCallback;
private final Queue<WeakReference<WeakConcurrentMap>> suppliedMaps = private final Queue<WeakReference<WeakConcurrentMap>> suppliedMaps =
new ConcurrentLinkedQueue<>(); new ConcurrentLinkedQueue<>();
@ -62,14 +62,16 @@ class WeakMapSuppliers {
WeakConcurrent() { WeakConcurrent() {
cleanerExecutorService = Executors.newScheduledThreadPool(1, THREAD_FACTORY); cleanerExecutorService = Executors.newScheduledThreadPool(1, THREAD_FACTORY);
shutdownCallback = new ShutdownCallback(cleanerExecutorService);
cleanerExecutorService.scheduleAtFixedRate( cleanerExecutorService.scheduleAtFixedRate(
new CleanupRunnable(cleanerExecutorService, suppliedMaps, finalized), new CleanupRunnable(cleanerExecutorService, shutdownCallback, suppliedMaps, finalized),
CLEAN_FREQUENCY_SECONDS, CLEAN_FREQUENCY_SECONDS,
CLEAN_FREQUENCY_SECONDS, CLEAN_FREQUENCY_SECONDS,
TimeUnit.SECONDS); TimeUnit.SECONDS);
try { try {
Runtime.getRuntime().addShutdownHook(new ShutdownCallback(cleanerExecutorService)); Runtime.getRuntime().addShutdownHook(shutdownCallback);
} catch (final IllegalStateException ex) { } catch (final IllegalStateException ex) {
// The JVM is already shutting down. // The JVM is already shutting down.
} }
@ -90,14 +92,17 @@ class WeakMapSuppliers {
private static class CleanupRunnable implements Runnable { private static class CleanupRunnable implements Runnable {
private final ScheduledExecutorService executorService; private final ScheduledExecutorService executorService;
private final Thread shutdownCallback;
private final Queue<WeakReference<WeakConcurrentMap>> suppliedMaps; private final Queue<WeakReference<WeakConcurrentMap>> suppliedMaps;
private final AtomicBoolean finalized; private final AtomicBoolean finalized;
public CleanupRunnable( public CleanupRunnable(
final ScheduledExecutorService executorService, final ScheduledExecutorService executorService,
final Thread shutdownCallback,
final Queue<WeakReference<WeakConcurrentMap>> suppliedMaps, final Queue<WeakReference<WeakConcurrentMap>> suppliedMaps,
final AtomicBoolean finalized) { final AtomicBoolean finalized) {
this.executorService = executorService; this.executorService = executorService;
this.shutdownCallback = shutdownCallback;
this.suppliedMaps = suppliedMaps; this.suppliedMaps = suppliedMaps;
this.finalized = finalized; this.finalized = finalized;
} }
@ -115,6 +120,7 @@ class WeakMapSuppliers {
} }
if (finalized.get() && suppliedMaps.isEmpty()) { if (finalized.get() && suppliedMaps.isEmpty()) {
executorService.shutdown(); executorService.shutdown();
Runtime.getRuntime().removeShutdownHook(shutdownCallback);
} }
} }
} }

View File

@ -107,7 +107,7 @@ class WeakConcurrentSupplierTest extends Specification {
// Hit map a few times to trigger unreferenced entries cleanup. // Hit map a few times to trigger unreferenced entries cleanup.
// Exact number of times that we need to hit map is implementation dependent. // Exact number of times that we need to hit map is implementation dependent.
// For Guava it i specified in // For Guava it is specified in
// com.google.common.collect.MapMakerInternalMap.DRAIN_THRESHOLD = 0x3F // com.google.common.collect.MapMakerInternalMap.DRAIN_THRESHOLD = 0x3F
if (name == "Guava" || name == "WeakInline") { if (name == "Guava" || name == "WeakInline") {
for (int i = 0; i <= 0x3F; i++) { for (int i = 0; i <= 0x3F; i++) {

View File

@ -60,6 +60,11 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
private final Map<String, String> defaultSpanTags; private final Map<String, String> defaultSpanTags;
/** A configured mapping of service names to update with new values */ /** A configured mapping of service names to update with new values */
private final Map<String, String> serviceNameMappings; private final Map<String, String> serviceNameMappings;
/**
* JVM shutdown callback, keeping a reference to it to remove this if DDTracer gets destroyed
* earlier
*/
private final Thread shutdownCallback;
/** Span context decorators */ /** Span context decorators */
private final Map<String, List<AbstractDecorator>> spanContextDecorators = private final Map<String, List<AbstractDecorator>> spanContextDecorators =
@ -163,15 +168,15 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
this.runtimeId = runtimeId; this.runtimeId = runtimeId;
this.serviceNameMappings = serviceNameMappings; this.serviceNameMappings = serviceNameMappings;
shutdownCallback =
new Thread() {
@Override
public void run() {
close();
}
};
try { try {
Runtime.getRuntime() Runtime.getRuntime().addShutdownHook(shutdownCallback);
.addShutdownHook(
new Thread() {
@Override
public void run() {
close();
}
});
} catch (final IllegalStateException ex) { } catch (final IllegalStateException ex) {
// The JVM is already shutting down. // The JVM is already shutting down.
} }
@ -200,6 +205,12 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
log.info("New instance: {}", this); log.info("New instance: {}", this);
} }
@Override
public void finalize() {
Runtime.getRuntime().removeShutdownHook(shutdownCallback);
shutdownCallback.run();
}
/** /**
* Returns the list of span context decorators * Returns the list of span context decorators
* *