From 4a33fa54d3b9218c33453ceb5b5a18699de195ef Mon Sep 17 00:00:00 2001 From: Besmir Beqiri Date: Thu, 11 Jul 2024 14:21:32 +0200 Subject: [PATCH] Some project gradle restructure and general cleanup (#7) * Move some gradle project constants under `gradle.properties` file * Enable JUnit 5 tests in Gradle * Run tests in GitHub CI builds * Set project minimal Java source and target compatibility to version `11` * Parametrize types for classes and methods * Replace with 'Comparator.comparing' * Use wildcard `?` to accept any type in the used set * Some code formatting and cleanup * Delete unused markdown file * Cleanup `build.gradle` * Update changelog * Remove parametrization from the `JMemoryBuddy#memoryTest` method * Remove parametrization from other classes and methods --- .github/workflows/gradle.yml | 20 ----- .github/workflows/main.yml | 24 ++++++ CHANGELOG.md | 5 ++ JMemoryBuddy.md | 3 - build.gradle | 28 ++++--- gradle.properties | 6 ++ .../sandec/jmemorybuddy/CleanupDetector.java | 23 +++--- .../de/sandec/jmemorybuddy/JMemoryBuddy.java | 76 +++++++++---------- .../sandec/jmemorybuddy/JMemoryBuddyLive.java | 13 ++-- .../jmemorybuddy/TestCleanupDetector.java | 9 ++- .../sandec/jmemorybuddy/TestJMemoryBuddy.java | 12 +-- .../jmemorybuddy/TestJMemoryBuddyLive.java | 8 +- 12 files changed, 120 insertions(+), 107 deletions(-) delete mode 100644 .github/workflows/gradle.yml create mode 100644 .github/workflows/main.yml delete mode 100644 JMemoryBuddy.md create mode 100644 gradle.properties diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml deleted file mode 100644 index c0b0fc3..0000000 --- a/.github/workflows/gradle.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Java CI - -on: [push] - -jobs: - build: - strategy: - matrix: - jvmversion: [8, 11, 17, 21] - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-java@v2 - with: - distribution: 'temurin' - java-version: '${{matrix.jvmversion}}' - - name: Build with Gradle - run: ./gradlew build diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..801bc5c --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,24 @@ +name: Build CI + +on: [push] + +jobs: + build: + strategy: + matrix: + jdk: [11, 17, 21] + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '${{matrix.jdk}}' + + - name: Build with Gradle + run: ./gradlew build + + - name: Run tests + run: ./gradlew test diff --git a/CHANGELOG.md b/CHANGELOG.md index 098c68a..5b51d76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +### 0.5.5 +* Parameterize classes and methods providing better type safety +* Set minimum Java version to 11 +* Enable JUnit 5 tests running in the CI builds + ### 0.5.4 * Slightly improved error messages * Updated CI to use also use Java17 and Java21 diff --git a/JMemoryBuddy.md b/JMemoryBuddy.md deleted file mode 100644 index 6fab4a8..0000000 --- a/JMemoryBuddy.md +++ /dev/null @@ -1,3 +0,0 @@ -##JMemoryBuddy - -Helps you \ No newline at end of file diff --git a/build.gradle b/build.gradle index 982491c..89d4374 100644 --- a/build.gradle +++ b/build.gradle @@ -2,11 +2,10 @@ buildscript { repositories { mavenCentral() } - } plugins { - id "io.github.gradle-nexus.publish-plugin" version "1.3.0" + id "io.github.gradle-nexus.publish-plugin" version "$NEXUS_PUBLISH_PLUGIN_VERSION" } repositories { @@ -19,30 +18,35 @@ apply plugin: 'signing' group = 'de.sandec' -archivesBaseName = "JMemoryBuddy" -version = '0.5.4' - -sourceCompatibility = 8 -targetCompatibility = 8 +archivesBaseName = "$ARCHIVE_BASE_NAME" +version = "$JMEMORYBUDDY_VERSION" java { + sourceCompatibility = 11 + targetCompatibility = 11 + withSourcesJar() withJavadocJar() } dependencies { - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' - testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.1' - //implementation 'org.openjdk.jol:jol-core:0.17' + testImplementation "org.junit.jupiter:junit-jupiter:$JUNIT_VERSION" + testImplementation "org.junit.jupiter:junit-jupiter-api:$JUNIT_VERSION" + testImplementation "org.junit.jupiter:junit-jupiter-params:$JUNIT_VERSION" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$JUNIT_VERSION" + testRuntimeOnly "ch.qos.logback:logback-classic:$LOGBACK_VERSION" } test { useJUnitPlatform() -} -test { testLogging { events "passed", "skipped", "failed" + showStandardStreams = true + showExceptions true + showCauses true + showStackTraces true + exceptionFormat "full" } } diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..0228208 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,6 @@ +JMEMORYBUDDY_VERSION=0.5.5-SNAPSHOT +ARCHIVE_BASE_NAME=JMemoryBuddy + +NEXUS_PUBLISH_PLUGIN_VERSION=1.3.0 +JUNIT_VERSION=5.10.3 +LOGBACK_VERSION=1.5.6 \ No newline at end of file diff --git a/src/main/java/de/sandec/jmemorybuddy/CleanupDetector.java b/src/main/java/de/sandec/jmemorybuddy/CleanupDetector.java index 2d825cf..cfaa555 100644 --- a/src/main/java/de/sandec/jmemorybuddy/CleanupDetector.java +++ b/src/main/java/de/sandec/jmemorybuddy/CleanupDetector.java @@ -1,13 +1,14 @@ package de.sandec.jmemorybuddy; -import java.lang.ref.WeakReference; import java.lang.ref.ReferenceQueue; -import java.util.HashSet; +import java.lang.ref.WeakReference; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; public class CleanupDetector { - private static HashSet references = new HashSet(); - private static ReferenceQueue queue = new ReferenceQueue();; + private static final Set references = ConcurrentHashMap.newKeySet(); + private static final ReferenceQueue queue = new ReferenceQueue<>(); static { Thread cleanupDetectorThread = new Thread(() -> { @@ -29,21 +30,23 @@ public class CleanupDetector { * The runnable gets executed after the object has been collected by the GC. */ public static void onCleanup(Object obj, Runnable r) { - onCleanup(new WeakReferenceWithRunnable(obj,r)); + onCleanup(new WeakReferenceWithRunnable(obj, r)); } + /** - * This version of the method can be used to provide more information + * This version of the method can be used to provide more information * in the heap dump by extending WeakReferenceWithRunnable. */ - public static void onCleanup(WeakReferenceWithRunnable weakref) { - references.add(weakref); + public static void onCleanup(WeakReferenceWithRunnable weakRef) { + references.add(weakRef); } /** * This class can be extended to provide more meta information to the method onCleanup. */ - public static class WeakReferenceWithRunnable extends WeakReference { - Runnable r = null; + public static class WeakReferenceWithRunnable extends WeakReference { + Runnable r; + WeakReferenceWithRunnable(Object ref, Runnable r) { super(ref, queue); this.r = r; diff --git a/src/main/java/de/sandec/jmemorybuddy/JMemoryBuddy.java b/src/main/java/de/sandec/jmemorybuddy/JMemoryBuddy.java index 9d73386..359ae88 100644 --- a/src/main/java/de/sandec/jmemorybuddy/JMemoryBuddy.java +++ b/src/main/java/de/sandec/jmemorybuddy/JMemoryBuddy.java @@ -1,6 +1,7 @@ package de.sandec.jmemorybuddy; import com.sun.management.HotSpotDiagnosticMXBean; + import javax.management.MBeanServer; import java.io.File; import java.io.IOException; @@ -11,8 +12,6 @@ import java.util.LinkedList; import java.util.Objects; import java.util.function.Consumer; -import java.util.function.Function; - /** * JMemoryBuddy provides various methods to test for memory leaks. @@ -21,11 +20,11 @@ */ public class JMemoryBuddy { - private static int steps; - private static int testDuration; - private static int sleepDuration; - private static boolean createHeapdump; - private static int garbageAmount = 999999; + private static final int steps; + private static final int testDuration; + private static final int sleepDuration; + private static final boolean createHeapdump; + private static final int garbageAmount; private static String mxBeanProxyName = "com.sun.management:type=HotSpotDiagnostic"; private static String outputFolderString = "."; @@ -49,7 +48,7 @@ private static String getDefaultOutputFolder() { } static void createGarbage() { - LinkedList list = new LinkedList(); + LinkedList list = new LinkedList<>(); int counter = 0; while (counter < garbageAmount) { counter += 1; @@ -61,7 +60,7 @@ static void createGarbage() { * Checks whether the content of the WeakReference can be collected. * @param weakReference The WeakReference to check. */ - public static void assertCollectable(WeakReference weakReference) { + public static void assertCollectable(WeakReference weakReference) { if (!checkCollectable(weakReference)) { AssertCollectable assertCollectable = new AssertCollectable(weakReference); createHeapDump(); @@ -74,11 +73,11 @@ public static void assertCollectable(WeakReference weakReference) { * @param weakReference The WeakReference to check. * @return Returns true, when the provided WeakReference can be collected. */ - public static boolean checkCollectable(WeakReference weakReference) { + public static boolean checkCollectable(WeakReference weakReference) { return checkCollectable(steps, weakReference) > 0; } - private static int checkCollectable(int stepsLeft, WeakReference weakReference) { + private static int checkCollectable(int stepsLeft, WeakReference weakReference) { int counter = stepsLeft; if (weakReference.get() != null) { @@ -100,7 +99,7 @@ private static int checkCollectable(int stepsLeft, WeakReference weakReference) } if (weakReference.get() == null && counter < steps / 3) { - int percentageUsed = (int) ((steps - counter) / steps * 100); + int percentageUsed = (steps - counter) / steps * 100; System.out.println("Warning test seems to be unstable. time used: " + percentageUsed + "%"); } @@ -111,7 +110,7 @@ private static int checkCollectable(int stepsLeft, WeakReference weakReference) * Checks whether the content of the WeakReference cannot be collected. * @param weakReference The WeakReference to check. */ - public static void assertNotCollectable(WeakReference weakReference) { + public static void assertNotCollectable(WeakReference weakReference) { if (!checkNotCollectable(weakReference)) { throw new AssertionError("Content of WeakReference was collected!"); } @@ -122,7 +121,7 @@ public static void assertNotCollectable(WeakReference weakReference) { * @param weakReference The WeakReference to check. * @return Returns true, when the provided WeakReference cannot be collected. */ - public static boolean checkNotCollectable(WeakReference weakReference) { + public static boolean checkNotCollectable(WeakReference weakReference) { createGarbage(); System.gc(); System.runFinalization(); @@ -134,17 +133,17 @@ public static boolean checkNotCollectable(WeakReference weakReference) { /** * A standard method to define a test which checks code for specific memory semantic. * The parameter of the lambda provides an API to define the required memory semantic. - * @param f A function which get's executed with the API to define the required memory semantic. + * @param f A function which gets executed with the API to define the required memory semantic. */ public static void memoryTest(Consumer f) { - LinkedList toBeCollected = new LinkedList(); - LinkedList toBeNotCollected = new LinkedList(); - LinkedList toBeReferenced = new LinkedList(); + LinkedList> toBeCollected = new LinkedList<>(); + LinkedList toBeNotCollected = new LinkedList<>(); + LinkedList toBeReferenced = new LinkedList<>(); f.accept(new MemoryTestAPI() { public void assertCollectable(Object ref) { Objects.requireNonNull(ref); - toBeCollected.add(new WeakReference(ref)); + toBeCollected.add(new WeakReference<>(ref)); } public void assertNotCollectable(Object ref) { Objects.requireNonNull(ref); @@ -159,7 +158,7 @@ public void setAsReferenced(Object ref) { int stepsLeft = steps; boolean failed = false; - for (WeakReference wRef: toBeCollected) { + for (WeakReference wRef: toBeCollected) { stepsLeft = checkCollectable(stepsLeft, wRef); } if (stepsLeft == 0) { @@ -172,10 +171,10 @@ public void setAsReferenced(Object ref) { } if (failed) { - LinkedList toBeCollectedMarked = new LinkedList(); - LinkedList toBeNotCollectedMarked = new LinkedList(); + LinkedList toBeCollectedMarked = new LinkedList<>(); + LinkedList toBeNotCollectedMarked = new LinkedList<>(); - for (WeakReference wRef: toBeCollected) { + for (WeakReference wRef: toBeCollected) { if (wRef.get() != null) { toBeCollectedMarked.add(new AssertCollectable(wRef)); } @@ -219,43 +218,43 @@ private static void setMxBeanProxyName(String mxBeanName) { private static HotSpotDiagnosticMXBean getHotspotMBean() throws IOException { MBeanServer server = ManagementFactory.getPlatformMBeanServer(); - HotSpotDiagnosticMXBean bean = - ManagementFactory.newPlatformMXBeanProxy(server, - mxBeanProxyName, HotSpotDiagnosticMXBean.class); - return bean; + return ManagementFactory.newPlatformMXBeanProxy(server, + mxBeanProxyName, HotSpotDiagnosticMXBean.class); } /** * This class provides different methods, which can be used to declare memory-constraints. * You can get an instance through the lambda of the method JMemoryBuddy.memoryTest. */ - public static interface MemoryTestAPI { + public interface MemoryTestAPI { + /** - * After executing the lambda, the provided ref must be collectable. Otherwise an Exception is thrown. + * After executing the lambda, the provided ref must be collectable. Otherwise, an Exception is thrown. * @param ref The reference which should be collectable. */ - public void assertCollectable(Object ref); + void assertCollectable(Object ref); + /** - * After executing the lambda, the provided ref must be not collectable. Otherwise an Exception is thrown. + * After executing the lambda, the provided ref must be not collectable. Otherwise, an Exception is thrown. * @param ref The reference which should not be collectable. */ - public void assertNotCollectable(Object ref); + void assertNotCollectable(Object ref); /** * The provided reference will be reference hard, so it won't be collected, until memoryTest finishes. * @param ref The reference which should get a hard reference for this test. */ - public void setAsReferenced(Object ref); + void setAsReferenced(Object ref); } static class AssertCollectable { - WeakReference assertCollectable; + WeakReference assertCollectable; - AssertCollectable(WeakReference ref) { + AssertCollectable(WeakReference ref) { this.assertCollectable = ref; } - WeakReference getWeakReference() { + WeakReference getWeakReference() { return assertCollectable; } @@ -267,7 +266,7 @@ public String toString() { } private static class AssertNotCollectable { - WeakReference assertNotCollectable; + WeakReference assertNotCollectable; String originalResultOfToString; AssertNotCollectable(Object ref) { @@ -275,7 +274,7 @@ private static class AssertNotCollectable { originalResultOfToString = ref.toString(); } - WeakReference getWeakReference() { + WeakReference getWeakReference() { return assertNotCollectable; } @@ -292,5 +291,4 @@ private static class SetAsReferenced { this.setAsReferenced = ref; } } - } diff --git a/src/main/java/de/sandec/jmemorybuddy/JMemoryBuddyLive.java b/src/main/java/de/sandec/jmemorybuddy/JMemoryBuddyLive.java index c69e71b..abef6e2 100644 --- a/src/main/java/de/sandec/jmemorybuddy/JMemoryBuddyLive.java +++ b/src/main/java/de/sandec/jmemorybuddy/JMemoryBuddyLive.java @@ -1,12 +1,11 @@ package de.sandec.jmemorybuddy; -import java.lang.ref.WeakReference; import java.util.*; import java.util.stream.Collectors; public class JMemoryBuddyLive { - static int collectedEntrys = 0; + static int collectedEntries = 0; static Set collectables = new HashSet<>(); /** @@ -18,15 +17,13 @@ synchronized static public void markCollectable(String name, Object ref) { Objects.requireNonNull(ref); CollectableEntry entry = new CollectableEntry(new Date(), name); - AssertCollectableLive pRef = new AssertCollectableLive(name, ref, () -> { - removeCollectable(entry); - }); + AssertCollectableLive pRef = new AssertCollectableLive(name, ref, () -> removeCollectable(entry)); collectables.add(entry); CleanupDetector.onCleanup(pRef); } private synchronized static void removeCollectable(CollectableEntry entry) { - collectedEntrys += 1; + collectedEntries += 1; collectables.remove(entry); } @@ -37,8 +34,8 @@ private synchronized static void removeCollectable(CollectableEntry entry) { */ synchronized static public Report getReport() { return new Report( - collectedEntrys, - collectables.stream().sorted((a, b) -> a.collectableSince.compareTo(b.collectableSince)) + collectedEntries, + collectables.stream().sorted(Comparator.comparing(a -> a.collectableSince)) .collect(Collectors.toList())); } diff --git a/src/test/java/de/sandec/jmemorybuddy/TestCleanupDetector.java b/src/test/java/de/sandec/jmemorybuddy/TestCleanupDetector.java index 2ec18c6..2c0dbde 100644 --- a/src/test/java/de/sandec/jmemorybuddy/TestCleanupDetector.java +++ b/src/test/java/de/sandec/jmemorybuddy/TestCleanupDetector.java @@ -1,7 +1,7 @@ package de.sandec.jmemorybuddy; -import de.sandec.jmemorybuddy.JMemoryBuddy; import org.junit.jupiter.api.Test; + import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -11,7 +11,7 @@ public class TestCleanupDetector { Runnable r = () -> latch.countDown(); @Test - public void isRunnableCalled() throws Exception{ + public void isRunnableCalled() throws Exception { JMemoryBuddy.memoryTest(checker -> { Object o = new Object(); CleanupDetector.onCleanup(o, r); @@ -23,9 +23,10 @@ public void isRunnableCalled() throws Exception{ @Test public void isPhantomRefCollectable() throws Exception { JMemoryBuddy.memoryTest(checker -> { - CleanupDetector.WeakReferenceWithRunnable pRef = new CleanupDetector.WeakReferenceWithRunnable(new Object(), () -> {}); + CleanupDetector.WeakReferenceWithRunnable pRef = + new CleanupDetector.WeakReferenceWithRunnable(new Object(), () -> { + }); CleanupDetector.onCleanup(pRef); - checker.assertCollectable(pRef); }); latch.await(1, TimeUnit.SECONDS); diff --git a/src/test/java/de/sandec/jmemorybuddy/TestJMemoryBuddy.java b/src/test/java/de/sandec/jmemorybuddy/TestJMemoryBuddy.java index c744025..7b9dc00 100644 --- a/src/test/java/de/sandec/jmemorybuddy/TestJMemoryBuddy.java +++ b/src/test/java/de/sandec/jmemorybuddy/TestJMemoryBuddy.java @@ -1,8 +1,8 @@ package de.sandec.jmemorybuddy; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import java.lang.ref.WeakReference; import java.util.List; @@ -35,7 +35,7 @@ public void simpleTest2() { @Test public void simpleTest3() { - for(int i = 0; i < 5; i += 1) { + for (int i = 0; i < 5; i += 1) { A referenced = new A(); JMemoryBuddy.memoryTest(checker -> { A notReferenced = new A(); @@ -54,7 +54,7 @@ public void simpleTest3() { @Test public void simpleTestRepeated() { - for(int i = 0; i < 100; i += 1) { + for (int i = 0; i < 100; i += 1) { A referenced = new A(); JMemoryBuddy.memoryTest(checker -> { A notReferenced = new A(); @@ -107,7 +107,7 @@ public void clearReferenceOnFinalization() { Object a = new Object(); Object o = new Object() { @Override - public void finalize() { + protected void finalize() { System.out.println("Finalized!"); ref[0] = null; } @@ -157,14 +157,14 @@ public void setReferenceOnFinalization() { Object[] a = new Object[1]; Object o = new Object() { @Override - public void finalize() { + protected void finalize() { System.out.println("Finalized!"); ref[0] = a[0]; a[0] = null; } }; a[0] = new Object(); - wref[0] = new WeakReference(a[0]); + wref[0] = new WeakReference<>(a[0]); checker.assertCollectable(o); System.out.println("Not collectable: " + a[0]); checker.assertNotCollectable(a[0]); diff --git a/src/test/java/de/sandec/jmemorybuddy/TestJMemoryBuddyLive.java b/src/test/java/de/sandec/jmemorybuddy/TestJMemoryBuddyLive.java index 5b22d30..507beb0 100644 --- a/src/test/java/de/sandec/jmemorybuddy/TestJMemoryBuddyLive.java +++ b/src/test/java/de/sandec/jmemorybuddy/TestJMemoryBuddyLive.java @@ -1,12 +1,10 @@ package de.sandec.jmemorybuddy; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; -import de.sandec.jmemorybuddy.JMemoryBuddy; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; public class TestJMemoryBuddyLive { + @Test public void testMarkKeepsGCBehaviour() throws InterruptedException { @@ -17,7 +15,7 @@ public void testMarkKeepsGCBehaviour() throws InterruptedException { checker.assertCollectable(o); }); Thread.sleep(50); // we might have a slight delay, - // because processing the collection might happen slightly delayed. + // because processing the collection might happen slightly delayed. Assertions.assertEquals(initialCollected + 1, JMemoryBuddyLive.getReport().collectedEntries); } }