2009-04-16 4 views
16

参照しているオブジェクトが占有しているメモリを含め、特定のJavaオブジェクトが占有しているメモリの量をプログラムで調べる必要があります。Javaオブジェクトが参照するオブジェクトを含むメモリが占​​有しているメモリをプログラムで計算する

私はメモリヒープダンプを生成し、ツールを使用して結果を分析することができます。ただし、ヒープ・ダンプを生成し、ダンプを読んでレポートを生成するには、かなりの時間がかかります。私はおそらくこれを何度もやる必要があるという事実を考えれば、私のプロジェクトでこの値を「ランタイム」とするコードを投げることができれば、私の仕事はもっと機敏になります。

どうすればこの問題を解決できますか?

PS:具体的に私はあなたがそのために反射を使用する必要があります型javax.xml.transform.Templates

+0

私はそれを正確な欺瞞と呼んでいません。 – Inisheer

+0

元の質問がこの文脈で使用可能な答えを提供することに失敗したように、ちょうどまったくの欺瞞ではありません... – Varkhan

+0

それはいくつかを提供しました。 – erickson

答えて

11

のオブジェクトのコレクションを持っています。 (

  • オブジェクトヘッダは8つのバイトを使用しています(それはすぐに私が構築していますGPLのツールキットの一部として利用できるようになりますが)私はここに投稿するためのコードの結果の一部はあまりにも複雑であるが、主なアイデアがあります
  • 各プリミティブフィールドは、実際のタイプに応じて1,2,4または8バイトを使用します。
  • 各オブジェクト参照(つまりプリミティブではない)フィールドは、4バイト(参照番号+参照オブジェクト使用)

あなたは別々に配列を扱う必要がありますヘッダーの長さ、長さフィールドの4バイト、テーブルの長さ4バイト、内部のオブジェクトが何であっても)。リフレクションを使用して、フィールド(およびその親フィールド)を反復処理する他のタイプのオブジェクトを処理する必要があります。

また、いくつかの場所で参照されているオブジェクトを何回も数えないように、再帰中に一連の "見た"オブジェクトを保持する必要があります。

+2

オブジェクトヘッダーはそれ以上の価値がありますが、古いJVMではもっと多く使うと思います。これは簡単なテストコード(100万個のオブジェクトを作成する、gc、少し眠る、空きメモリをチェックする、理論的には動作保証されていない、実際には検証する)は簡単です。 – StaxMan

+2

また、4バイトは32ビットのリファレンス用です。 64ビットシステムの場合、8バイトと思う。 – StaxMan

+0

さて、私はそれもしばらく考えていましたが、それについての参考文献は見つかりませんでした...だから、StaxManの説明どおりのベンチマークを試みました。 – Varkhan

3

良い汎用ソリューションは、ヒープサイズデルタを使用することです。これは、最小限の労力を要し、任意のタイプのオブジェクト/オブジェクトグラフ間で再利用可能である。オブジェクトを何回もインスタンス化して破棄し、その間でガーベッジ・コレクションを行い、平均をとることで、結果を変更してかなり正確な結果を得るコンパイラとJVMの最適化を避けることができます。バイトまで正確な答えが必要な場合、これはあなたのための解決策ではないかもしれませんが、私が知っているすべての実際のアプリケーション(プロファイリング、メモリ要件の計算)では非常にうまく動作します。以下のコードはそれだけです。

public class Sizeof { 
     public static void main(String[] args) 
      throws Exception { 
     // "warm up" all classes/methods that we are going to use: 
     runGC(); 
     usedMemory(); 

     // array to keep strong references to allocated objects: 
     final int count = 10000; // 10000 or so is enough for small ojects 
     Object[] objects = new Object[count]; 

     long heap1 = 0; 

     // allocate count+1 objects, discard the first one: 
     for (int i = -1; i < count; ++i) { 
      Object object; 

    //// INSTANTIATE YOUR DATA HERE AND ASSIGN IT TO 'object': 


      object=YOUR OBJECT; 
    ////end your code here 
      if (i >= 0) { 
      objects[i] = object; 
      } 
      else { 
      object = null; // discard the "warmup" object 
      runGC(); 
      heap1 = usedMemory(); // take a "before" heap snapshot 
      } 
     } 

     runGC(); 
     long heap2 = usedMemory(); // take an "after" heap snapshot: 

     final int size = Math.round(((float)(heap2 - heap1))/count); 
     System.out.println("'before' heap: " + heap1 + 
          ", 'after' heap: " + heap2); 
     System.out.println("heap delta: " + (heap2 - heap1) + 
          ", {" + objects[0].getClass() + "} size = " + size + " bytes"); 
     } 

     // a helper method for creating Strings of desired length 
     // and avoiding getting tricked by String interning: 
     public static String createString(final int length) { 
     final char[] result = new char[length]; 
     for (int i = 0; i < length; ++i) { 
      result[i] = (char)i; 
     } 

     return new String(result); 
     } 

     // this is our way of requesting garbage collection to be run: 
     // [how aggressive it is depends on the JVM to a large degree, but 
     // it is almost always better than a single Runtime.gc() call] 
     private static void runGC() 
      throws Exception { 
     // for whatever reason it helps to call Runtime.gc() 
     // using several method calls: 
     for (int r = 0; r < 4; ++r) { 
      _runGC(); 
     } 
     } 

     private static void _runGC() 
      throws Exception { 
     long usedMem1 = usedMemory(), usedMem2 = Long.MAX_VALUE; 

     for (int i = 0; (usedMem1 < usedMem2) && (i < 1000); ++i) { 
      s_runtime.runFinalization(); 
      s_runtime.gc(); 
      Thread.currentThread().yield(); 

      usedMem2 = usedMem1; 
      usedMem1 = usedMemory(); 
     } 
     } 

     private static long usedMemory() { 
     return s_runtime.totalMemory() - s_runtime.freeMemory(); 
     } 

     private static final Runtime s_runtime = Runtime.getRuntime(); 

    } // end of class 
+1

これは、gc()メソッドとrunFinalization()メソッドが決定的ではないという事実に欠陥があります。これらのアクションを実行するためのヒントは、実行時のヒントです。ランタイムがそれらを無視することは完全に合法です。 –

+1

それは欠陥がありません - あなたが何回言っているように、時々無視され、反復回数が多く、結果が平均化されます。 – Peter

+1

あなたは少しでもそれを微調整したとしても、少なくともこのコードのソースを信用している可能性があります:http://www.javaworld.com/javaworld/javatips/jw-javatip130.html – wolfcastle

6

これはすでにClassmexerと呼ばれています。

私は自分で試したことはありませんが、私は自分自身を転がす前にそのルートに行きます。

+1

リンクの+1。 –