ThreadLocal内存泄漏问题实践(一)
针对
ThreadLocal为什么要设计成private static
深入分析 ThreadLocal 内存泄漏问题
实践ThreadLocal的内存泄漏问题:
package JVM;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class TestThreadLocal {
private static final int LEN = 10;
public static void main(String[] args) throws InterruptedException {
ExecutorService ex = Executors.newFixedThreadPool(LEN);
TestThread testThread = new TestThread() {
@Override
protected void finalize() throws Throwable {
System.out.println("testThread回收");
}
};
for(int i=0; i<LEN; ++i) {
ex.execute(testThread);
}
TestThread.countDownLatch.await();
System.out.println("任务1完成");
TestThread.threadLocal = null;
System.gc();
System.out.println("第1次gc");
TestThread.threadLocal = new ThreadLocal<TestKey>();
TestThread.countDownLatch = new CountDownLatch(10);
for(int i=0; i<LEN; ++i) {
ex.execute(testThread);
}
TestThread.countDownLatch.await();
System.out.println("任务2完成");
System.gc();
System.out.println("第2次gc");
}
private static class TestKey {
Integer key;
public TestKey(Integer integer) {
key = integer;
}
@Override
public String toString() {
return String.valueOf(key);
}
@Override
protected void finalize() throws Throwable {
System.out.println("回收TestKey:"+key);
}
};
private static class TestThread implements Runnable {
private static AtomicInteger atomicInteger = new AtomicInteger(0);
public static volatile CountDownLatch countDownLatch = new CountDownLatch(10);
public static volatile ThreadLocal<TestKey> threadLocal = new ThreadLocal<TestKey>() {
@Override
protected void finalize() throws Throwable {
System.out.println("回收threadlocal:" + this);
}
};
@Override
public void run() {
TestKey key = new TestKey(atomicInteger.incrementAndGet());
threadLocal.set(key);
System.out.println("线程:"+Thread.currentThread()+":"+ key);
// threadLocal.remove();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
}
}
}
线程:Thread[pool-1-thread-1,5,main]:1
线程:Thread[pool-1-thread-5,5,main]:2
线程:Thread[pool-1-thread-2,5,main]:3
线程:Thread[pool-1-thread-4,5,main]:4
线程:Thread[pool-1-thread-3,5,main]:5
线程:Thread[pool-1-thread-6,5,main]:6
线程:Thread[pool-1-thread-7,5,main]:7
线程:Thread[pool-1-thread-8,5,main]:8
线程:Thread[pool-1-thread-9,5,main]:9
线程:Thread[pool-1-thread-10,5,main]:10
任务1完成
[GC (System.gc()) [PSYoungGen: 2165K->560K(7168K)] 2165K->568K(100352K), 0.0028411 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 560K->0K(7168K)] [ParOldGen: 8K->434K(93184K)] 568K->434K(100352K), [Metaspace: 3397K->3397K(1056768K)], 0.0131344 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
第1次gc
线程:Thread[pool-1-thread-2,5,main]:11
线程:Thread[pool-1-thread-8,5,main]:13
线程:Thread[pool-1-thread-6,5,main]:12
回收threadlocal:JVM.TestThreadLocal$TestThread$1@554dfb98
线程:Thread[pool-1-thread-7,5,main]:19
线程:Thread[pool-1-thread-5,5,main]:18
线程:Thread[pool-1-thread-4,5,main]:17
线程:Thread[pool-1-thread-3,5,main]:16
线程:Thread[pool-1-thread-10,5,main]:15
线程:Thread[pool-1-thread-9,5,main]:14
线程:Thread[pool-1-thread-1,5,main]:20
任务2完成
[GC (System.gc()) [PSYoungGen: 1229K->128K(7168K)] 1664K->562K(100352K), 0.0006910 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 128K->0K(7168K)] [ParOldGen: 434K->418K(93184K)] 562K->418K(100352K), [Metaspace: 3398K->3398K(1056768K)], 0.0082321 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
第2次gc
没回收,与《多线程实战P154页结果》不一致,不过书上也表明该试验在jdk7下,若在jdk8下进行未必得到同样的结果