2011-07-19 21 views
14

Javaプログラムの自動メモリリーク検出について考えていました。ループは、メモリが少しずつ忍び寄るかどうかを確認するために使用されているJavaでのメモリリークの自動検出

Call System.gc() several times 
Determine initial heap memory consumption using either Runtime class or JMX 
Loop 
    Do something that exercises program under test 
End loop 

Call System.gc() several times 
Determine final heap memory consumption 
Compare initial and final memory numbers 

:基本的なアルゴリズムは、次のロジックが含まれているJUnitsを作成することです。

予想されるメモリ使用量と予想外のメモリ使用量を区別する必要があります。

これは実際に単体テストではありません。しかし、JUnitフレームワークは使いやすいです。

このアプローチは有効だと思いますか? このアプローチはメモリリークの特定にはうまくいくと思いますか? このようなことをしたことがありますか?

+0

私は一度、あまりにもこのようなテストを考えていたが、良いsolution..hopefully 1は、ここに出てくる見つけることができませんでした。 – mort

答えて

6

javaでこれを行うことはできません。ガベージコレクタは、必要であると判断したときに実行されます。また、これに加えて、再利用できるようにメモリを "フリー"にすることもできますが、ブロックの割り当てを解除することを意味するわけではありません。

0

一度だけ作成しなければならないワンタイムオブジェクトを削除するには、テストを実行する前にまずテストを実行する必要があります。そう:

test() 
checkMem() 
test() 
checkMem() 
compareIfMemUsageHasNotIncreased() 
4

それはJavaで意味のあるアプローチではありません。 System.gc()はあなたに合理的な保証を与えるものではなく、問題があると自分自身で確信したとしても、この方法は問題の発見に役立つものではありません。

代わりにメモリプロファイラを使用する必要があります。プロフェッショナルの代価を払わない場合は、jvisualvmをJVMと一緒にインストールしてみてください。

+0

JConsoleにはJDKが付属しているので、ほとんどの使用例ではそれ以上のものだと思います。 – Dunes

+0

JConsoleは本当に素晴らしいです! Netbeansには、便利なプロファイリングツールも付属しています。 – mort

+0

私はNetbeansプロファイラとJConsole/jvisualvmは同じツールであり、後者はNetbeansから独立していると思います。 –

17

私は信頼性の高いメモリリークのための単体テストフレームワークを開発しました。基本的な考え方は、ガベージコレクションされるべきオブジェクトへの弱い参照を作成し、テストを実行し、完全なGCを実行し、弱い参照がクリアされていることを確認することです。ここで

は私のフレームワークを使用して、かなり一般的な回帰テストです:

public void testDS00032554() throws Exception { 
    Project testProject = getTestProject(); 
    MemoryLeakVerifier verifier = new MemoryLeakVerifier(new RuntimeTestAction(getTestClassMap())); 
    testProject.close(); 
    verifier.assertGarbageCollected("RuntimeTestAction should be garbage collected when project closed"); 
} 

ここで注意すべきいくつかのものがあります。

  1. あなたが持っているしたいオブジェクトを収集することが重要であるべきでありませんあなたのテストの終わりまで保持されるので、単体テストの変数に格納されます。
  2. これは、リークが報告され、どのオブジェクトを削除すべきかを知っている回帰テストに役立つテクニックです。
  3. このアプローチの1つの問題は、なぜテストが失敗したかを判断することが難しいことです。この時点で、メモリプロファイラが必要になります(私はYourKitの一部です)。しかし、IMOは、漏れが将来誤って再導入されることがないように、回帰テストを行うことは依然として有効です。

ここでフルだ:メソッドは、現在( Java Tip 130: Do you know your data size?が、この記事で説明したように)失敗する前にGCを何度も実行しようとしますので、

  • 私は、すべての参照がすぐに消去されていないと、いくつかのスレッド化の問題に遭遇しましたあなたはそれを試してみたい場合にはヘルパークラス:

    /** 
    * A simple utility class that can verify that an object has been successfully garbage collected. 
    */ 
    public class MemoryLeakVerifier { 
    private static final int MAX_GC_ITERATIONS = 50; 
    private static final int GC_SLEEP_TIME  = 100; 
    
    private final WeakReference reference; 
    
    public MemoryLeakVerifier(Object object) { 
        this.reference = new WeakReference(object); 
    } 
    
    public Object getObject() { 
        return reference.get(); 
    } 
    
    /** 
    * Attempts to perform a full garbage collection so that all weak references will be removed. Usually only 
    * a single GC is required, but there have been situations where some unused memory is not cleared up on the 
    * first pass. This method performs a full garbage collection and then validates that the weak reference 
    * now has been cleared. If it hasn't then the thread will sleep for 50 milliseconds and then retry up to 
    * 10 more times. If after this the object still has not been collected then the assertion will fail. 
    * 
    * Based upon the method described in: http://www.javaworld.com/javaworld/javatips/jw-javatip130.html 
    */ 
    public void assertGarbageCollected(String name) { 
        Runtime runtime = Runtime.getRuntime(); 
        for (int i = 0; i < MAX_GC_ITERATIONS; i++) { 
         runtime.runFinalization(); 
         runtime.gc(); 
         if (getObject() == null) 
          break; 
    
         // Pause for a while and then go back around the loop to try again... 
         try { 
          EventQueue.invokeAndWait(Procedure.NoOp); // Wait for the AWT event queue to have completed processing 
          Thread.sleep(GC_SLEEP_TIME); 
         } catch (InterruptedException e) { 
          // Ignore any interrupts and just try again... 
         } catch (InvocationTargetException e) { 
          // Ignore any interrupts and just try again... 
         } 
        } 
        PanteroTestCase.assertNull(name + ": object should not exist after " + MAX_GC_ITERATIONS + " collections", getObject()); 
    } 
    

    }

  • +0

    これは私がネット上で見つけることができる最高のものです。私はすぐにそれを受け入れるでしょう:) – wuppi

    +0

    @Andy、なぜThread.sleep()が必要ですか?ガベージコレクションを続けるだけでは十分ではありませんか? – Gili

    +1

    Thread.sleepの意図は、ガベージコレクタに追いつくのに少し時間を与えることです。コードがスリープしていない場合、ガベージコレクタが完了するまでCPUをペグするタイトループになります。 –

    関連する問題