现象
MAC
unable to creat new native thread
window
测试前 在启动测试类之前先将JVM内存调整小一点,不然很容易将电脑跑出问题
在idea里:Run -> Edit Configurations VM options修改成-Xms10M -Xmx10M
(-Xms10M Java Heap内存初始化值 -Xmx10M Java Heap内存最大值)
Exception in thread “main” java.lang.OutOfMemoryError: Java heap space。
代码
public static void main(String[] args) throws InterruptedException {
AtomicInteger i = new AtomicInteger(0);
for (; ; ) {
Executors.newFixedThreadPool(10).submit(() -> System.out.println(i.getAndIncrement()));
Thread.sleep(4);
}
}
循环创建线程池 而并没有关闭线程池,导致线程池的实例会一直存在
源码
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE); // MAX_VALUE = 0x7fffffff
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
原因分析
Java常见的GC Root
Java 进行GC的时候会从GC root进行可达性判断,常见的GC Root有如下:
- 通过System Class Loader或者Boot Class Loader加载的class对象,通过自定义类加载器加载的class不一定是GC Root
- 处于激活状态的线程
- 栈中的对象
- JNI栈中的对象
- JNI中的全局对象
- 正在被用于同步的各种锁对象
- JVM自身持有的对象,比如系统类加载器等。
在调查内存泄漏原因的时候可以根据GC Root来推导
当执行一个Runnable时,会先创建一个ThreadPoolExecutor中的内部类Worker对象,将这个Runnable对象作为Worker对象的一个成员变量
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
private volatile ThreadFactory threadFactory;
public ThreadFactory getThreadFactory() {
return threadFactory;
}
所以 当线程在执行的时候引用关系如下ThreadPoolExecutor->Worker->thread
一个运行的线程是作为GC ROOT的,不会被GC; 所以要主动shutdown
newSingleThreadExecutor不会有此问题,因为FinalizableDelegatedExecutorService 重写了finalize函数,也就是说这个类会在被GC回收之前,先执行线程池的shutdown方法。
java.util.concurrent.Executors
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
static class FinalizableDelegatedExecutorService
extends DelegatedExecutorService {
FinalizableDelegatedExecutorService(ExecutorService executor) {
super(executor);
}
protected void finalize() {
super.shutdown();
}
}