phenomenon
MAC
unable to creat new native thread
window
Before testing, adjust the JVM memory to a smaller size before starting the test class, otherwise it is easy to run the computer out of the problem.
In the idea: Run -> Edit Configurations VM options to -Xms10M -Xmx10M
(- Xms10M Java Heap memory initialization value-Xmx10M Java Heap memory maximum)
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);
}
}
The thread pool is created circularly without closing the thread pool, causing the instance of the thread pool to always exist
Source code
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);
}
Cause Analysis
Common GC Root in
Java When Java performs GC, it will judge the reachability from the GC root. Common GC Roots are as follows:
- Class objects loaded by System Class Loader or Boot Class Loader, classes loaded by custom class loader are not necessarily GC Root
- Active thread
- Objects on the stack
- Objects in the JNI stack
- Global objects in JNI
- Various lock objects being used for synchronization
- Objects held by the JVM itself, such as the system class loader, etc.
When investigating the cause of memory leaks, it can be derived from GC Root
When a Runnable is executed, an internal Worker object in ThreadPoolExecutor will be created first, and this Runnable object will be used as a member variable of the Worker object
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;
}
So when a thread in the implementation of a reference to the following relationship ThreadPoolExecutor-> Worker-> thread
thread that runs as GC ROOT will not be GC; so take the initiative to shutdown
newSingleThreadExecutor does not have this problem, because FinalizableDelegatedExecutorService rewrites the finalize function, which means that this class will execute the shutdown method of the thread pool before being recycled by the GC.
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();
}
}
Precautions for the use of thread pools
ThreadPoolExecutor-How does the thread pool ensure that threads are not destroyed