Skip to content

Commit 2442b1f

Browse files
authored
Introduce canary class to detect when we've done enough to trigger class unloading (#8967)
1 parent 089cf8f commit 2442b1f

File tree

2 files changed

+37
-8
lines changed

2 files changed

+37
-8
lines changed

dd-java-agent/src/test/groovy/datadog/trace/agent/InstrumenterUnloadTest.groovy

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,26 @@ class InstrumenterUnloadTest extends Specification {
2626
, ["DD_API_KEY": API_KEY]
2727
, new PrintStream(testOutput))
2828

29-
int unloadCount = 0
29+
boolean canaryUnloaded = false
30+
int unloadedInstrumentationCount = 0
3031
new ByteArrayInputStream((testOutput.toByteArray())).eachLine {
3132
System.out.println(it)
33+
if (it =~ /(?i)unload.*Canary/) {
34+
canaryUnloaded = true
35+
}
3236
if (it =~ /(?i)unload.* datadog.trace.instrumentation./) {
33-
unloadCount++
37+
unloadedInstrumentationCount++
3438
}
3539
}
3640

41+
if (!canaryUnloaded) {
42+
System.out.println("WARNING: Canary class was not unloaded!")
43+
}
44+
3745
then:
3846
returnCode == 0
39-
unloadCount > 0
47+
// skip check if we couldn't even unload our Canary class, as that
48+
// indicates full GC didn't happen enough to trigger any unloading
49+
!canaryUnloaded || unloadedInstrumentationCount > 0
4050
}
4151
}
Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,32 @@
11
package jvmbootstraptest;
22

3+
import static java.util.concurrent.TimeUnit.MINUTES;
4+
35
import datadog.trace.test.util.GCUtils;
6+
import java.lang.management.ClassLoadingMXBean;
7+
import java.lang.management.ManagementFactory;
48

59
public class UnloadingChecker {
6-
public static void main(final String[] args) {
7-
try {
8-
GCUtils.awaitGC();
9-
} catch (InterruptedException e) {
10-
e.printStackTrace();
10+
static class Canary {}
11+
12+
public static void main(final String[] args) throws Exception {
13+
ClassLoadingMXBean classLoadingMXBean = ManagementFactory.getClassLoadingMXBean();
14+
long initialUnloadCount = classLoadingMXBean.getUnloadedClassCount();
15+
16+
// load an isolated class which we know can be unloaded after a full GC
17+
new IsolatingClassLoader().loadClass("jvmbootstraptest.UnloadingChecker$Canary");
18+
19+
long waitNanos = MINUTES.toNanos(2);
20+
long startNanos = System.nanoTime();
21+
22+
while (System.nanoTime() - startNanos < waitNanos) {
23+
try {
24+
GCUtils.awaitGC();
25+
} catch (Throwable ignore) {
26+
}
27+
if (initialUnloadCount < classLoadingMXBean.getUnloadedClassCount()) {
28+
break; // some class unloading has taken place, stop and check results
29+
}
1130
}
1231
}
1332
}

0 commit comments

Comments
 (0)