aboutsummaryrefslogtreecommitdiffstats
path: root/tests/bugs1922
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 /tests/bugs1922
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>
Diffstat (limited to 'tests/bugs1922')
-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
4 files changed, 130 insertions, 0 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")
+ }
+}