2015-10-27 6 views
6

25000スレッドを割り当ててから解放する小さなアプリケーションがあります。 スレッドが解放されている間、アプリケーションのメモリ消費量は増加し、すべてのスレッドが終了した後も高く保たれます。 スレッドを解放するときにJavaがヒープからメモリをリークする

トップ

は次のようになります。 -heap jmapのは、このようになります

PID USER  PR NI VIRT RES SHR S %CPU %MEM  TIME+ COMMAND 
9133 root  20 0 22.601g 8.612g 12080 S 0.0 9.1 1:18.61 java 

ながら:

Attaching to process ID 9133, please wait... 
Debugger attached successfully. 
Server compiler detected. 
JVM version is 25.66-b17 

using thread-local object allocation. 
Parallel GC with 18 thread(s) 

Heap Configuration: 
    MinHeapFreeRatio   = 40 
    MaxHeapFreeRatio   = 100 
    MaxHeapSize    = 1073741824 (1024.0MB) 
    NewSize     = 104857600 (100.0MB) 
    MaxNewSize    = 104857600 (100.0MB) 
    OldSize     = 968884224 (924.0MB) 
    NewRatio     = 2 
    SurvivorRatio   = 8 
    MetaspaceSize   = 21807104 (20.796875MB) 
    CompressedClassSpaceSize = 1073741824 (1024.0MB) 
    MaxMetaspaceSize   = 1073741824 (1024.0MB) 
    G1HeapRegionSize   = 0 (0.0MB) 

Heap Usage: 
PS Young Generation 
Eden Space: 
    capacity = 78643200 (75.0MB) 
    used  = 1572864 (1.5MB) 
    free  = 77070336 (73.5MB) 
    2.0% used 
From Space: 
    capacity = 13107200 (12.5MB) 
    used  = 0 (0.0MB) 
    free  = 13107200 (12.5MB) 
    0.0% used 
To Space: 
    capacity = 13107200 (12.5MB) 
    used  = 0 (0.0MB) 
    free  = 13107200 (12.5MB) 
    0.0% used 
PS Old Generation 
    capacity = 968884224 (924.0MB) 
    used  = 1264416 (1.205841064453125MB) 
    free  = 967619808 (922.7941589355469MB) 
    0.13050227970271916% used 

808 interned Strings occupying 54648 bytes. 

私の知る限り見ることができるように、jmapはレポートには何も存在しないことトップで報告された8.612gを説明することができます。

Javaのバージョンは、Oracle 1.8.0_66

あるアプリケーションは、Red Hat Enterprise Linuxサーバー上で実行されます(マイポを)7.1をリリース。

アプリケーションのコードは以下の通りです:

import java.util.*; 
import java.util.concurrent.atomic.AtomicInteger; 

public class WaitTest { 
    static AtomicInteger ai = new AtomicInteger(0); 

    public static void main(String[] args) { 
     List<WaitingThread> threads = new LinkedList<>(); 
     while (true) { 
      System.out.println("number of threads: " + ai.get()); 
      String s = System.console().readLine(); 
      if (s == null) 
       System.exit(0); 
      s = s.trim(); 
      if (s.isEmpty()) 
       continue; 
      char command = s.charAt(0); 
      if (command != '+' && command != '-') { 
       System.out.println("+ or - please"); 
       continue; 
      } 
      String num = s.substring(1); 
      int iNum; 
      try { 
       iNum = Integer.parseInt(num.trim()); 
      } catch (Exception ex) { 
       System.out.println("valid number please"); 
       continue; 
      } 
      if (command == '+') { 
       for (int i = 0; i < iNum; i++) { 
        WaitingThread t = new WaitingThread(); 
        t.start(); 
        threads.add(t); 
       } 
      } 
      if (command == '-') { 
       Set<WaitingThread> threadsToJoin = new HashSet<>(); 
       for (Iterator<WaitingThread> it = threads.iterator(); it.hasNext();) { 
        if (iNum > 0) { 
         WaitingThread t = it.next(); 
         threadsToJoin.add(t); 

         synchronized (t.lock) { 
          t.lock.notify(); 
         } 

         it.remove(); 
         iNum--; 
        } else 
         break; 

       } 
       for (WaitingThread t : threadsToJoin) 
        try { 
         t.join(); 
        } catch (InterruptedException e) { 
         e.printStackTrace(); 
        } 
      } 
      System.gc(); 
     } 
    } 

    static class WaitingThread extends Thread { 
     public final Object lock = new Object(); 

     @Override 
     public void run() { 
      ai.incrementAndGet(); 
      try { 
       deepStack(200); 
      } catch (InterruptedException ex) { 
      } catch (Exception ex) { 
       System.out.println("exception in thread " + Thread.currentThread().getName() + ", " + ex.getMessage()); 
      } finally { 
       ai.decrementAndGet(); 
      } 
     } 

     private void deepStack(int depth) throws InterruptedException { 
      if (depth == 0) { 
       synchronized (lock) { 
        lock.wait(); 
       } 

      } else 
       deepStack(depth - 1); 
     } 
    } 

} 

Here is a link to pmap output:

Here is a link to jmap -dump (hprof)

+1

あなたは、プロセスのヒープ・ダンプをadquireしなければならない –

+1

後に出力をのpmap PID' 'から。 http://man7.org/linux/manage/man1/pmap.1.html –

+0

あなたのスレッドへのすべての参照をthreadsT​​oJoinに保持しているのを見てください。スレッドが完了してもメモリに残ります... – nafas

答えて

0

は、すべてのスレッドが最初のコマンドの実行中に作成されてはいけない "+" リスト変数で'threads'ました範囲外になることはありませんか?つまり、リンクされたリストはすべてのスレッドへの参照を保持しており、Main()が終了するまでガベージコレクションされません。また、possible that the LinkedList itself is leaking memoryです。それが問題ではない場合でも、おそらくusing an ArrayList instead of a LinkedListと考えるべきです。

さらに、finding memory leaks in Javaについて質問する別の質問があります。

+0

Kelly、リスト変数 'threads'は " - "コマンド中にクリアされます。また、添付のhprofファイルで、 'threads'リストが完全に空であることがわかります。 – Amir

+0

ArrayListの使用に切り替えて、それがメモリ使用量に影響するかどうか確認できますか? –

+0

ArrayListに切り替えました。 メモリ使用量に大きな影響はありません。 – Amir

0

アプリケーションメモリは、ヒープサイズに緩やかに結合されています。 GCが実行され、処理がクリーンアップされると、システムメモリではなくヒープがクリーンアップされます。実際、ヒープスペースを頻繁に解放するにもかかわらず、システムメモリを減らすためにシステムコールを実行しないJVMもあります。

0

各スレッドには、Javaヒープの一部ではないスタック(割り当てられたメモリ)が必要です。

スタックのデフォルトサイズは、JVMのバージョン、OSなどに依存..あなたは-Xss JVMパラメータに合わせ、それをすることができます

25000スレッドが簡単に8.6グラムアミールのコメント次

更新説明することができます:確か

100%ではないが、ツアーのpmapログを見ては、あなたはおそらくアリーナアロケータ内に問題がありますglibc

すべての64Mバイトの割り当てあなたのglibcのバージョンが> 2であることを

チェックを参照してください。 。10

は、環境変数MALLOC_ARENA_MAX = 2セットを使用してプログラムを実行してみてください。あなたは実際の値で遊ぶことができます。

+0

ありがとう、benbenw、 しかし、すべてのスレッドが終了した後でもアプリケーションのメモリ消費量が高くなっているのはなぜですか? – Amir

+0

は私が/lib/libc.so.6を実行することで、私のglibcのバージョンをチェックする私の答え – benbenw

+0

benbenw、 を更新しました。 2.17です。 私は= 1000 MALLOC_ARENA_MAXで、その後MALLOC_ARENA_MAX = 10、 で、その後MALLOC_ARENA_MAX = 2、 で、その後MALLOC_ARENA_MAX = 1、 で私のプログラムを実行しました。 同じ結果です。 – Amir

関連する問題