Skip to content

Commit ba75789

Browse files
committed
Issue#809: Fix not working garbage collection
Signed-off-by: Drophoff <[email protected]>
1 parent 1966186 commit ba75789

File tree

3 files changed

+115
-17
lines changed

3 files changed

+115
-17
lines changed

simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/DefaultExports.java

+55-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
package io.prometheus.client.hotspot;
22

3+
import java.util.Arrays;
4+
import java.util.Collections;
5+
import java.util.List;
6+
7+
import io.prometheus.client.Collector;
38
import io.prometheus.client.CollectorRegistry;
49

510
/**
@@ -18,6 +23,18 @@
1823
public class DefaultExports {
1924

2025
private static boolean initialized = false;
26+
27+
private final static List<Collector> defaultCollectors = Collections.unmodifiableList(Arrays.asList(
28+
new BufferPoolsExports(),
29+
new ClassLoadingExports(),
30+
new CompilationExports(),
31+
new GarbageCollectorExports(),
32+
new MemoryAllocationExports(),
33+
new MemoryPoolsExports(),
34+
new StandardExports(),
35+
new ThreadExports(),
36+
new VersionInfoExports()
37+
));
2138

2239
/**
2340
* Register the default Hotspot collectors with the default
@@ -30,19 +47,48 @@ public static synchronized void initialize() {
3047
initialized = true;
3148
}
3249
}
50+
51+
/**
52+
* Release the resources used by the default Hotspot
53+
* collectors. It is safe to call this method multiple times,
54+
* as this will unregister all collectors once.
55+
*/
56+
public static synchronized void terminate() {
57+
if (initialized) {
58+
unregister(CollectorRegistry.defaultRegistry);
59+
initialized = false;
60+
}
61+
}
3362

3463
/**
3564
* Register the default Hotspot collectors with the given registry.
65+
*
66+
* @param registry to which the collector is added. Null values ​​are
67+
* not allowed.
3668
*/
3769
public static void register(CollectorRegistry registry) {
38-
new BufferPoolsExports().register(registry);
39-
new ClassLoadingExports().register(registry);
40-
new CompilationExports().register(registry);
41-
new GarbageCollectorExports().register(registry);
42-
new MemoryAllocationExports().register(registry);
43-
new MemoryPoolsExports().register(registry);
44-
new StandardExports().register(registry);
45-
new ThreadExports().register(registry);
46-
new VersionInfoExports().register(registry);
70+
for (Collector collector : defaultCollectors) {
71+
collector.register(registry);
72+
73+
if (collector instanceof MemoryAllocationExports) {
74+
((MemoryAllocationExports)collector).addGarbageCollectorListener();
75+
}
76+
}
77+
}
78+
79+
/**
80+
* Unregister the default Hotspot collectors from the given registry.
81+
*
82+
* @param registry from that the default collectors are removed. Null
83+
* values ​​are not allowed.
84+
*/
85+
public static void unregister(CollectorRegistry registry) {
86+
for (Collector collector : defaultCollectors) {
87+
registry.unregister(collector);
88+
89+
if (collector instanceof MemoryAllocationExports) {
90+
((MemoryAllocationExports)collector).removeGarbageCollectorListener();
91+
}
92+
}
4793
}
4894
}

simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/MemoryAllocationExports.java

+37-7
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,19 @@
55
import io.prometheus.client.Collector;
66
import io.prometheus.client.Counter;
77

8+
import javax.management.ListenerNotFoundException;
89
import javax.management.Notification;
910
import javax.management.NotificationEmitter;
1011
import javax.management.NotificationListener;
1112
import javax.management.openmbean.CompositeData;
1213
import java.lang.management.GarbageCollectorMXBean;
1314
import java.lang.management.ManagementFactory;
1415
import java.lang.management.MemoryUsage;
16+
import java.util.ArrayList;
1517
import java.util.HashMap;
1618
import java.util.List;
1719
import java.util.Map;
20+
import java.util.logging.Logger;
1821

1922
public class MemoryAllocationExports extends Collector {
2023
private final Counter allocatedCounter = Counter.build()
@@ -23,18 +26,45 @@ public class MemoryAllocationExports extends Collector {
2326
.labelNames("pool")
2427
.create();
2528

29+
private static final Logger LOGGER = Logger.getLogger(MemoryAllocationExports.class.getName());
30+
31+
private NotificationListener listener = null;
32+
2633
public MemoryAllocationExports() {
27-
AllocationCountingNotificationListener listener = new AllocationCountingNotificationListener(allocatedCounter);
28-
for (GarbageCollectorMXBean garbageCollectorMXBean : getGarbageCollectorMXBeans()) {
29-
if (garbageCollectorMXBean instanceof NotificationEmitter) {
30-
((NotificationEmitter) garbageCollectorMXBean).addNotificationListener(listener, null, null);
31-
}
32-
}
34+
addGarbageCollectorListener();
3335
}
3436

3537
@Override
3638
public List<MetricFamilySamples> collect() {
37-
return allocatedCounter.collect();
39+
return null == listener ? new ArrayList<Collector.MetricFamilySamples>() : allocatedCounter.collect();
40+
}
41+
42+
void removeGarbageCollectorListener() {
43+
if (null != listener ) {
44+
for (GarbageCollectorMXBean garbageCollectorMXBean : getGarbageCollectorMXBeans()) {
45+
if (garbageCollectorMXBean instanceof NotificationEmitter) {
46+
try {
47+
((NotificationEmitter) garbageCollectorMXBean).removeNotificationListener(listener);
48+
} catch (ListenerNotFoundException e) {
49+
LOGGER.warning("The default notification listener could not be removed from the garbage collector.");
50+
}
51+
}
52+
}
53+
54+
listener = null;
55+
}
56+
}
57+
58+
void addGarbageCollectorListener() {
59+
if (null == listener) {
60+
listener = new AllocationCountingNotificationListener(allocatedCounter);
61+
62+
for (GarbageCollectorMXBean garbageCollectorMXBean : getGarbageCollectorMXBeans()) {
63+
if (garbageCollectorMXBean instanceof NotificationEmitter) {
64+
((NotificationEmitter) garbageCollectorMXBean).addNotificationListener(listener, null, null);
65+
}
66+
}
67+
}
3868
}
3969

4070
static class AllocationCountingNotificationListener implements NotificationListener {

simpleclient_hotspot/src/test/java/io/prometheus/client/hotspot/MemoryAllocationExportsTest.java

+23-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.prometheus.client.hotspot;
22

3+
import io.prometheus.client.Collector.MetricFamilySamples;
34
import io.prometheus.client.Counter;
45
import org.junit.Test;
56
import org.mockito.Mockito;
@@ -11,6 +12,8 @@
1112
import java.util.List;
1213

1314
import static org.junit.Assert.assertEquals;
15+
import static org.junit.Assert.assertTrue;
16+
import static org.mockito.Mockito.times;
1417

1518
public class MemoryAllocationExportsTest {
1619

@@ -62,7 +65,26 @@ protected List<GarbageCollectorMXBean> getGarbageCollectorMXBeans() {
6265
}
6366
};
6467

65-
Mockito.verify((NotificationEmitter) notificationEmitterMXBean).addNotificationListener(Mockito.any(MemoryAllocationExports.AllocationCountingNotificationListener.class), Mockito.isNull(), Mockito.isNull());
68+
Mockito.verify((NotificationEmitter) notificationEmitterMXBean).addNotificationListener(Mockito.any(MemoryAllocationExports.AllocationCountingNotificationListener.class), Mockito.nullable(NotificationFilter.class), Mockito.nullable(Object.class));
6669
Mockito.verifyNoInteractions(notNotificationEmitterMXBean);
6770
}
71+
72+
@Test
73+
public void testEmptyMetricsWithRemovedNotificationEmitter() {
74+
MemoryAllocationExports allocationExporter = new MemoryAllocationExports();
75+
allocationExporter.removeGarbageCollectorListener();
76+
77+
List<MetricFamilySamples> metrics = allocationExporter.collect();
78+
79+
assertTrue("Metrics should be empty, if no notification emitter is registered", metrics.isEmpty());
80+
}
81+
82+
@Test
83+
public void testAddNotificationEmitterNotCalledMultipleTimesDuringInstantiation() {
84+
MemoryAllocationExports allocationExporter = Mockito.mock(MemoryAllocationExports.class);
85+
86+
allocationExporter.addGarbageCollectorListener();
87+
88+
Mockito.verify(allocationExporter, times(1)).addGarbageCollectorListener();
89+
}
6890
}

0 commit comments

Comments
 (0)