スレッドプールのOOM例外

現象

MACが
現象
新しいネイティブスレッド
ウィンドウを作成できない
テストする前に、テストクラスを開始する前にJVMメモリを小さいサイズに調整してください。そうしないと、問題からコンピュータを簡単に実行できます。
アイデア:実行->構成の編集VMオプションの変更〜-Xms10M -Xmx10M
(-Xms10MJavaヒープメモリ初期化値-Xmx10MJavaヒープメモリ最大)
ウィンドウの実行

スレッド「メイン」の例外java.lang.OutOfMemoryError:Javaヒープスペース。
代码

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ルートJavaがGCを実行すると、GCルートから到達可能性が判断されます。一般的なGCルートは次のとおりです。

  1. システムクラスローダーまたはブートクラスローダーによってロードされるクラスオブジェクト、カスタムクラスローダーによってロードされるクラスは、必ずしもGCルートである必要はありません。
  2. アクティブスレッド
  3. スタック上のオブジェクト
  4. JNIスタック内のオブジェクト
  5. JNIのグローバルオブジェクト
  6. 同期に使用されているさまざまなロックオブジェクト
  7. システムクラスローダーなど、JVM自体が保持するオブジェクト。
    メモリリークの原因を調査する場合、GCルートから導き出すことができます

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->
GC ROOTとして実行されるスレッドスレッドはGCではないため、主導権を握ってシャットダウンします。

FinalizableDelegatedExecutorServiceがfinalize関数を書き換えるため、newSingleThreadExecutorにはこの問題はありません。つまり、このクラスは、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();
        }
    }

スレッドプールの使用に関する注意事項

ThreadPoolExecutor-スレッドプールはどのようにしてスレッドが破壊されないようにしますか

おすすめ

転載: blog.csdn.net/eluanshi12/article/details/109240465