aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Kriegisch <Alexander@Kriegisch.name>2024-04-10 11:03:13 +0200
committerAlexander Kriegisch <Alexander@Kriegisch.name>2024-04-10 11:23:00 +0200
commit856db5d97f329041751418b2cc43d7574e26144d (patch)
tree10893c2586a7b98fc5d48473a23499c32ba53be8
parente54ae565842527ac8cd55807649a32d17dec627e (diff)
downloadaspectj-856db5d97f329041751418b2cc43d7574e26144d.tar.gz
aspectj-856db5d97f329041751418b2cc43d7574e26144d.zip
Add IT reproducing JoinPointImpl thread-locals memory leak
Relates to #302. Signed-off-by: Alexander Kriegisch <Alexander@Kriegisch.name>
-rw-r--r--tests/bugs1922/github_302/FirstAspect.aj12
-rw-r--r--tests/bugs1922/github_302/NestedAroundClosureMemoryLeakTest.java86
-rw-r--r--tests/bugs1922/github_302/SecondAspect.aj12
-rw-r--r--tests/bugs1922/github_302/Task.java20
-rw-r--r--tests/src/test/java/org/aspectj/systemtest/ajc1922/Bugs1922Tests.java4
-rw-r--r--tests/src/test/resources/org/aspectj/systemtest/ajc1922/ajc1922.xml14
6 files changed, 146 insertions, 2 deletions
diff --git a/tests/bugs1922/github_302/FirstAspect.aj b/tests/bugs1922/github_302/FirstAspect.aj
new file mode 100644
index 000000000..4d99110db
--- /dev/null
+++ b/tests/bugs1922/github_302/FirstAspect.aj
@@ -0,0 +1,12 @@
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+
+@Aspect
+public class FirstAspect {
+ @Around("execution(* toIntercept())")
+ public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
+ //System.out.println(getClass().getSimpleName());
+ return joinPoint.proceed();
+ }
+}
diff --git a/tests/bugs1922/github_302/NestedAroundClosureMemoryLeakTest.java b/tests/bugs1922/github_302/NestedAroundClosureMemoryLeakTest.java
new file mode 100644
index 000000000..9b3b2f828
--- /dev/null
+++ b/tests/bugs1922/github_302/NestedAroundClosureMemoryLeakTest.java
@@ -0,0 +1,86 @@
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class NestedAroundClosureMemoryLeakTest {
+
+ private static final int NUM_THREAD_POOLS = 4;
+ private static final int THREAD_POOL_SIZE = 3;
+
+ public static void main(String[] args) throws Exception {
+ testNoMemoryLeak_ThreadLocalCleared();
+ }
+
+ /**
+ * Tests that the thread-locals of the spawned threads are either null or contain all null elements
+ */
+ public static void testNoMemoryLeak_ThreadLocalCleared() throws Exception {
+ List<ExecutorService> executorServices = createExecutorServicesWithFixedThreadPools();
+ try {
+ executeTasks(executorServices);
+
+ Field mapField = Thread.class.getDeclaredField("threadLocals");
+ mapField.setAccessible(true);
+ Set<Thread> threads = Thread.getAllStackTraces().keySet();
+ System.out.println("Number of pool threads = " + threads.stream().filter(thread -> thread.getName().contains("pool")).count());
+
+ threads.stream()
+ .filter(thread -> thread.getName().contains("pool"))
+ .forEach(thread -> {
+ try {
+ Object threadLocals = mapField.get(thread);
+ if (threadLocals != null) {
+ Field tableField = threadLocals.getClass().getDeclaredField("table");
+ tableField.setAccessible(true);
+ Object[] threadLocalTable = (Object[]) tableField.get(threadLocals);
+ if (threadLocalTable != null) {
+ for (Object entry : threadLocalTable) {
+ if (entry == null)
+ continue;
+ Field entryValueField = entry.getClass().getDeclaredField("value");
+ entryValueField.setAccessible(true);
+ throw new RuntimeException(
+ "All thread-locals should be null, but found entry with value " + entryValueField.get(entry)
+ );
+ }
+ }
+ }
+ }
+ catch (RuntimeException rte) {
+ throw rte;
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+
+ System.out.println("Test passed - all thread-locals are null");
+ }
+ finally {
+ for (ExecutorService executorService : executorServices)
+ executorService.shutdown();
+ }
+ }
+
+ private static List<ExecutorService> createExecutorServicesWithFixedThreadPools() {
+ List<ExecutorService> executorServiceList = new ArrayList<>(NestedAroundClosureMemoryLeakTest.NUM_THREAD_POOLS);
+ for (int i = 0; i < NestedAroundClosureMemoryLeakTest.NUM_THREAD_POOLS; i++)
+ executorServiceList.add(Executors.newFixedThreadPool(THREAD_POOL_SIZE));
+ return executorServiceList;
+ }
+
+ private static void executeTasks(List<ExecutorService> executorServices) throws Exception {
+ for (ExecutorService executorService : executorServices) {
+ for (int i = 0; i < THREAD_POOL_SIZE * 2; i++)
+ new Task(executorService).doSomething();
+ }
+ System.out.println("Finished executing tasks");
+
+ // Sleep to take a memory dump
+ // Thread.sleep(500000);
+ }
+
+}
diff --git a/tests/bugs1922/github_302/SecondAspect.aj b/tests/bugs1922/github_302/SecondAspect.aj
new file mode 100644
index 000000000..5f088b63a
--- /dev/null
+++ b/tests/bugs1922/github_302/SecondAspect.aj
@@ -0,0 +1,12 @@
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+
+@Aspect
+public class SecondAspect {
+ @Around("execution(* toIntercept())")
+ public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
+ //System.out.println(getClass().getSimpleName());
+ return joinPoint.proceed();
+ }
+}
diff --git a/tests/bugs1922/github_302/Task.java b/tests/bugs1922/github_302/Task.java
new file mode 100644
index 000000000..0d1ea6402
--- /dev/null
+++ b/tests/bugs1922/github_302/Task.java
@@ -0,0 +1,20 @@
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+public class Task {
+ final ExecutorService taskManager;
+
+ public Task(final ExecutorService executorService) {
+ taskManager = executorService;
+ }
+
+ public void doSomething() throws ExecutionException, InterruptedException {
+ Future<?> future = taskManager.submit(Task::toIntercept);
+ future.get();
+ }
+
+ public static void toIntercept() {
+ //System.out.println("Executing task")
+ }
+}
diff --git a/tests/src/test/java/org/aspectj/systemtest/ajc1922/Bugs1922Tests.java b/tests/src/test/java/org/aspectj/systemtest/ajc1922/Bugs1922Tests.java
index 06211a179..6e402fa11 100644
--- a/tests/src/test/java/org/aspectj/systemtest/ajc1922/Bugs1922Tests.java
+++ b/tests/src/test/java/org/aspectj/systemtest/ajc1922/Bugs1922Tests.java
@@ -15,8 +15,8 @@ import org.aspectj.testing.XMLBasedAjcTestCase;
*/
public class Bugs1922Tests extends XMLBasedAjcTestCase {
- public void testDummy() {
- //runTest("dummy");
+ public void testGitHub_302() {
+ runTest("thread-local around closure index is removed after innermost proceed");
}
public static Test suite() {
diff --git a/tests/src/test/resources/org/aspectj/systemtest/ajc1922/ajc1922.xml b/tests/src/test/resources/org/aspectj/systemtest/ajc1922/ajc1922.xml
index d436032e2..e10fd9975 100644
--- a/tests/src/test/resources/org/aspectj/systemtest/ajc1922/ajc1922.xml
+++ b/tests/src/test/resources/org/aspectj/systemtest/ajc1922/ajc1922.xml
@@ -196,4 +196,18 @@
</run>
</ajc-test>
+ <!-- https://github.com/eclipse-aspectj/aspectj/issues/302 -->
+ <ajc-test dir="bugs1922/github_302" title="thread-local around closure index is removed after innermost proceed">
+ <compile files="NestedAroundClosureMemoryLeakTest.java Task.java FirstAspect.aj SecondAspect.aj" options="-1.8 -XnoInline"/>
+ <run class="NestedAroundClosureMemoryLeakTest" vmargs="--add-opens java.base/java.lang=ALL-UNNAMED">
+ <stdout>
+ <line text="Finished executing tasks"/>
+ <line text="Number of pool threads = 12"/>
+ <line text="Test passed - all thread-locals are null"/>
+ </stdout>
+ <!-- No RuntimeException on stderr-->
+ <stderr/>
+ </run>
+ </ajc-test>
+
</suite>