diff --git a/src/test/java/software/amazon/awssdk/crt/test/CrtMemoryLeakDetector.java b/src/test/java/software/amazon/awssdk/crt/test/CrtMemoryLeakDetector.java index a04971dab..955c19cf3 100644 --- a/src/test/java/software/amazon/awssdk/crt/test/CrtMemoryLeakDetector.java +++ b/src/test/java/software/amazon/awssdk/crt/test/CrtMemoryLeakDetector.java @@ -65,6 +65,38 @@ public static void nativeMemoryLeakCheck() throws Exception { } } + /** + * Checks for JVM memory leaks by comparing memory usage before and after test. + * A significant increase in memory usage indicates a potential leak. + * + * @param memoryBeforeTest The JVM memory usage before the test started + * @throws Exception if a memory leak is detected + */ + public static void jvmMemoryLeakCheck(long memoryBeforeTest) throws Exception { + long memoryAfterTest = getJvmMemoryInUse(); + long memoryGrowth = memoryAfterTest - memoryBeforeTest; + + // Allow some tolerance for normal memory fluctuations (5MB) + long maxAllowedGrowth = expectedFixedGrowth(); + + if (memoryGrowth > maxAllowedGrowth) { + String output = String.format( + "Potential JVM Memory Leak Detected!\n" + + "Memory before test: %d bytes (%.2f MB)\n" + + "Memory after test: %d bytes (%.2f MB)\n" + + "Memory growth: %d bytes (%.2f MB)\n" + + "Allowed growth: %d bytes (%.2f MB)\n", + memoryBeforeTest, memoryBeforeTest / (1024.0 * 1024.0), + memoryAfterTest, memoryAfterTest / (1024.0 * 1024.0), + memoryGrowth, memoryGrowth / (1024.0 * 1024.0), + maxAllowedGrowth, maxAllowedGrowth / (1024.0 * 1024.0) + ); + + Log.log(Log.LogLevel.Error, Log.LogSubject.JavaCrtGeneral, output); + Assert.fail(output); + } + } + public static void leakCheck(Callable fn) throws Exception { leakCheck(DEFAULT_NUM_LEAK_TEST_ITERATIONS, expectedFixedGrowth(), fn); } diff --git a/src/test/java/software/amazon/awssdk/crt/test/CrtTestFixture.java b/src/test/java/software/amazon/awssdk/crt/test/CrtTestFixture.java index 1194a64ac..773daf4ec 100644 --- a/src/test/java/software/amazon/awssdk/crt/test/CrtTestFixture.java +++ b/src/test/java/software/amazon/awssdk/crt/test/CrtTestFixture.java @@ -32,6 +32,7 @@ public class CrtTestFixture { private CrtTestContext context; + private long jvmMemoryBeforeTest; @Rule public TestName testName = new TestName(); @@ -239,6 +240,13 @@ public void setup() { System.out.println("[TEST START] " + testName.getMethodName()); Log.log(Log.LogLevel.Debug, LogSubject.JavaCrtGeneral, "CrtTestFixture setup begin"); + // Capture initial JVM memory usage for leak detection + if (CRT.getOSIdentifier() != "android") { + // Force GC to get a stable baseline + Runtime.getRuntime().gc(); + jvmMemoryBeforeTest = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); + } + // TODO this CrtTestContext should be removed as we are using System Properties // for tests now. context = new CrtTestContext(); @@ -260,8 +268,9 @@ public void tearDown() { try { Runtime.getRuntime().gc(); CrtMemoryLeakDetector.nativeMemoryLeakCheck(); + CrtMemoryLeakDetector.jvmMemoryLeakCheck(jvmMemoryBeforeTest); } catch (Exception e) { - throw new RuntimeException("Memory leak from native resource detected!"); + throw new RuntimeException("Memory leak detected!"); } } Log.log(Log.LogLevel.Debug, LogSubject.JavaCrtGeneral, "CrtTestFixture tearDown end");