• 締切済み

オブジェクトのヒープサイズの取得

任意のオブジェクトの実行中のヒープサイズを取得する方法を教えてください。 具体的には、Undoバッファ(LinkedList)を実装していて、そのヒープサイズが あるサイズを超えた場合には、HDDに内容を保存してヒープを開放したいと考えています。 これを実現するために、動的にヒープサイズを取得する必要があるのですが、 また、上記のようにListがあるサイズを超えたら自動的にHDDなどに保存・呼び出しをしてくれて ユーザレベルでオンメモリがどうか等を意識しなくてもすむようなライブラリは 存在しないでしょうか? 以上2点について、よろしくお願いします。

みんなの回答

回答No.7

ANo.6で挙げられたふたつの条件に加えて、 (3)OutOfMemoryErrorが起こりうるすべてのコードが、OutOfMemoryErrorが発生し処理が中断しても不整合が生じないように設計されていること という条件が必要だと思います。 そうでなければOutOfMemoryErrorが発生したそのオブジェクトは壊れているとみなさざるを得ません。 たとえば、ArrayList.set() で IndexOutOfBoundsException が発生しても、 それは予期された動作ですからArrayListは壊れていませんが、 OutOfMemoryErrorが起きたらそのArrayListは壊れていて、 もはや使い物にならないとみなしたほうがいいでしょう。 JavaのAPIにもOutOfMemoryErrorを予期しているメソッドほとんどないです。その大半が使えないでしょう。 >大きなデータオブジェクトを単純にアロケートしていくだけのアプリケーションでは、通常、満たされます とのことですが、「大きなデータオブジェクトを単純にアロケートしていくだけのアプリケーション」などまず存在しませんし、 「すべてのオブジェクトは、OutOfMemoryErrorが起きた際にエラーハンドリングが可能であるくらいに、 サイズが大きくなければならない」という奇妙な制限を加えてまでその方法をとる利点はないと思います。 また、JavaのAPIの大半は小さなオブジェクトをアロケートする可能性があるので、使用を避けねばならなくなります。 >事前に大きなヘッジメモリをアロケートしておいて、OOME時にそれを解放します。 そのような方法でなんとかエラーのハンドリングできるとしても、 エラーメッセージを表示したりエラーログを残すぐらいがせいぜいでしょう。 エラー発生直前に実行されていたコードはその機能を完遂することなく突然中断され、 不整合な状態になっている可能性がありますから、そのようなアプリケーションの実行を続けるのは危険です。 壊れたデータで大切なファイルを上書きしてしまうといった取り返しのつかないことをしてしまう前に、 アプリケーションを終了してしまったほうが安全でしょう。 >「メモリウォッチャースレッド」を動かす技法 >SoftReferenceやWeakReference そうですね。質問者のかたの要件には合いませんけれど、キャッシュを実現するならそれらが適当だと思います。 これらに比べれば、OutMfMemoryErrorのcatchなど冗談もいいところです。 >アプリケーションの設計を見直す(もっとメモリセーフな設計にする) それが可能なら最初からその設計をするべきです。 ですから、OutMfMemoryErrorのcatchなどせず、最初から安全な設計にすべきだと自分は言っているわけです。 >OOMEで単純にアプリケーションを終わらせる、などの結論になるでしょう。 その結論では質問者のかたが困ってしまいますよ。 >実例として具体的にご紹介できるオープンソースのソフト等は、今の私の知識内にはありません。 残念です……。 Google codesearch( http://www.google.com/codesearch ) で簡単に漁ってみたところ、 幾つか OutOfMemoryError を予期しているメソッドやcatchしているコードは見つかりました。 たとえばGNU classpthのjava.lang.reflect.Array#newInstance()がOutOfMemoryErrorを投げることがドキュメントに書かれていました。 catchしているものは、むしろThrowableからOutOfMemoryErrorをふるいわけ、 再びOutOfMemoryErrorを投げるために使われていることが多いようです。 OutOfMemoryErrorのハンドリングは難しいからでしょう。

全文を見る
すると、全ての回答が全文表示されます。
  • _ranco_
  • ベストアンサー率58% (126/214)
回答No.6

たしかに、hirusagariさんがおっしゃるように、どんな技法にも…コンピュータやプログラミングに限らず…その技法が有効に使えるための前提条件がありますね。「OutOfMemoryErrorをcatchする」に関しては、(1)もれなく確実にcatchできること、と、(2)そのときに、有意義な処理ができる程度のメモリ残量があることが、基本的な前提条件です。これらの前提条件は、大きなデータオブジェクトを単純にアロケートしていくだけのアプリケーションでは、通常、満たされます。西暦2000年出版のJava Pitfallsで紹介されて以来、ときどき見かける(私も書いたことがある!)コードですが、実例として具体的にご紹介できるオープンソースのソフト等は、今の私の知識内にはありません。 そして、上の(1)の条件が満たされない場合、つまりOOMEが不特定多数の場所で予測不能に起こりうる場合には、独立のバックグラウンドスレッドとして「メモリウォッチャースレッド」を動かす技法があります。これは、一定の閾値(たとえば5メガバイト)を決めておいて、空きメモリの大きさがそれを下回ったら適切な対策を取ります(たとえば大きなデータ構造をclearする)。 また、(2)の条件が満たされない、つまりOOMEが起きたときメモリの余裕がない場合は、「ヘッジング」という技法がよく使われます。いわゆる、“ヘッジファンド”の“ヘッジ”です。事前に大きなヘッジメモリをアロケートしておいて、OOME時にそれを解放します。ヘッジを取り崩すわけですね。Eclipseが、この技法を使っているようです。もちろん、適切な対策を実行したあとで、ヘッジを再びアロケートしなければなりません。 OOME対策として上の三つの技法が使えない状況では、これまたよく見かける方法ですが、データオブジェクト(等)のパーシステンス化(バックアップ)は早期にやっておいて、それらのオブジェクトの参照をSoftReferenceやWeakReferenceに変えます。WeakReferenceを使っているMapであるjava.util.WeakHashMapを、便利に使えるアプリケーションもあるでしょう。 以上のどれも不可能なら、アプリケーションの設計を見直す(もっとメモリセーフな設計にする)、あるいは、OOMEで単純にアプリケーションを終わらせる、などの結論になるでしょう。

全文を見る
すると、全ての回答が全文表示されます。
回答No.5

本項の質問文にあるように「HDDに内容を保存してヒープを開放したい」わけですから、 OutOfMemoryErrorが起きたときに簡単なログを残すサンプルを書いてみます。 import java.io.*; import java.util.*; public class Test {   public static void main(String[] args) throws Exception{     LinkedList<byte[]> list = new LinkedList<byte[]>();     Runtime runtime = Runtime.getRuntime();     for(;;){       try{         list.add(new byte[0]);       }catch(OutOfMemoryError e){         File file = new File("log.txt");         FileWriter writer = new FileWriter(file, true);         writer.write(String.format("OutOfMemoryError occurred. The list size is %d. Free memory is %d byte.\n", list.size(), runtime.freeMemory()));         writer.close();         list.clear();       }     }   } } これを結果を早く出すために -Xmx2048 のVMオプションをつけて、Windows XP上のJRE1.6.0_01で実行してみました。 すると new File() するところで再びOutOfMemoryErrorが発生し終了しました。 new byte[0] のところを new byte[1000000] ぐらいに書き換えて実行するとcatchで復旧しそのまま実行を続けられます。 これは new byte[1000000] が失敗したとき、まだある程度のメモリが残されているためですね。 それに対して new byte[0] で少しずつメモリを確保していくと、 new File() もできないくらいのまさに「ギリチョンのメモリ貧乏」になってしまいます。 つまり、大量にメモリを確保したときにうまくOutOfMemoryErrorすれば復旧できますが、 少量メモリを確保したときに運悪くOutOfMemoryErrorが発生すると落ちます。 確率の問題になり、とても安定しているとはいえないと思いますから、 自分ならそのようなコーディングを避けると思います。 そして、メモリの確保はコードのあらゆる部分で行われる可能性があり、 従ってOutOfMemoryErrorはコードのあらゆる位置で発生するおそれがありますから、 Undo/Redoの部分だけOutOfMemoryErrorをcatchしてもザル同然です。 コードのどの位置でOutOfMemoryErrorが発生しても復旧できるように書かねばならず、 自分の考えではそれはとてつもなく厄介なコーディングになる思います。 >多くの実用アプリでよく使われている よろしかったらどのアプリケーションで使われているか教えていただけませんか? ソースが公開されているものでしたら幸いです。 そのようなアプリケーションのコードがどうやって安定してOutOfMemoryErrorに対処しているのか学びたいと思いますので。

全文を見る
すると、全ての回答が全文表示されます。
  • _ranco_
  • ベストアンサー率58% (126/214)
回答No.4

OutOfMemoryErrorをcatchしてメモリを解放するやりかたで、実用的/経験的にはOKですし、多くの実用アプリでよく使われている技法です。理由は、(1)物理的明示的にオンメモリの存在がはっきりしているリソースを解放するから、そして、(2)通常は、JVMがまったく何もできなくなるほどギリチョンのメモリ貧乏になることはないからです。 あなたのアプリの実際のニーズや実行シナリオに即して、下のようなテストプログラムを書いて動かしてみてください。 --------------------------- (全角スペース→半角スペースに) import java.util.*; public class OomeRecovery{  public static void main(String[] args){   ArrayList<Double[]> ad = new ArrayList<Double[]>();   Runtime rt = Runtime.getRuntime();   String s = "";   int counter = 100;   while (--counter > 0){    s = "";    System.out.printf("Before: %12d ", rt.freeMemory());    try{     Double[] da = new Double[1000000];     ad.add(da);    }    catch (OutOfMemoryError e){     ad.clear();     s = " *";    }    System.out.printf("After : %12d%s\n", rt.freeMemory(), s);   }  } } ----------------------------

全文を見る
すると、全ての回答が全文表示されます。
回答No.3

#2です。 >Instrumentationのインスタンスの取得の方法がわかりませんでした。 #2で挙げたページに次のような記述があるのでお読みになりましたよね。 <quote> Instrumentation インタフェースのインスタンスを取得する方法は 2 つあります。 1.エージェントクラスを指定する方法で JVM を起動した場合。この場合、Instrumentation インスタンスは、そのエージェントクラスの premain メソッドに渡されます。 2.JVM の開始後にエージェントを開始する機構が JVM に用意されている場合。この場合、Instrumentation インスタンスは、そのエージェントコードの agentmain メソッドに渡されます。 これらの機構は、パッケージの仕様 で説明します。 </quote> また、上記で言及されているパッケージの仕様に以下の記述があるのをお読みになりましたよね。 <quote> JVM はエージェントクラスで最初に次のメソッドを呼び出そうとします。 public static void premain(String agentArgs, Instrumentation inst); </quote> Instrumentationのインスタンスはpremainの引数として取得できるようです。 従ってサンプルコードをおこすほどの内容ではありませんし、むしろ上記の「エージェント」の内容や起動方法がポイントのようです。 この記述のどの部分がわからないのでしょうか? わからない部分を具体的に挙げていただければお答えできるかもしれません。 ただし自分はこのパッケージは使ったことがないので、Javadocから読み取れるもの以外の内容は自分はお答えしかねます。 あと#1さんの補足になりますが、OutOfMemoryErrorが発生している状態でまともにコレクションをclear()できる保証はありません。 ましてやファイルを開いてUndoバッファを書き込みなんてとても出来そうにないですし、 正常にアプリケーションを復帰できる期待はもてませんのでお勧めはしません。

全文を見る
すると、全ての回答が全文表示されます。
回答No.2

オブジェクトのサイズは java.lang.instrument.Instrumentation#getObjectSize で計れるようです。 http://java.sun.com/javase/ja/6/docs/ja/api/java/lang/instrument/Instrumentation.html#getObjectSize(java.lang.Object) >ユーザレベルでオンメモリがどうか等を意識しなくてもすむ OSが提供する仮想メモリではだめでしょうか?

ggable
質問者

補足

ご回答ありがとうございました。 しかしながら、Instrumentationのインスタンスの取得の 方法がわかりませんでした。 Instrumentationのインスタンスを取得して、それを使って Listのヒープサイズを調べるところまでのサンプルを ご提供いただけないでしょうか? 以上、よろしくお願いします。

全文を見る
すると、全ての回答が全文表示されます。
  • _ranco_
  • ベストアンサー率58% (126/214)
回答No.1

OutOfMemoryErrorをcatchして、LinkedList(など)をclear()するのが簡単ですね。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • java.util.Listのヒープサイズの取得

    オブジェクトのサイズを取得するメソッドとして、  java.lang.instrument.Instrumentation#getObjectSize があることは教わったのですが、なんと、このメソッドは参照先オブジェクトの サイズを調べてくれません。 ですから、私の環境では、Listに何のオブジェクトをaddしても、しなくても 24 が返ってくるだけでした。 どなたか、List内のオブジェクトの合計のヒープサイズを求めるライブラリもしくは、 リフレクションとInstrumentation#getObjectSizeを使った合計のヒープサイズを調べる 具体的な実装例をご存知の方はおられませんでしょうか? ( 前の質問は全く無関係な方向に進んでしまっていますので、   しばらく放置させていただきます。ご了承ください。)

  • 無効なオブジェクトライブラリ

    エクセルVBAにて、ソフトを作製しています。 急に「無効なオブジェクトライブラリです。または定義されていないオブジェクトへの参照を含んでいます」というエラーが出てくるようになり、すべてのプロシージャが実行出来ない状況に陥ってしまします。 そうなってしまったら、全て閉じて最終保存したところまで戻らなければならず、非常にイラつきます。 中身ですが、ユーザーフォームは特に使っておらず、Sheet内のフォームのコマンドボタン、またコントロール/ツールボックスからリストボックスとテキストボックスを使っています。 ソースの中身について ・SQL接続を使っています。 ・開く度にテキストボックスを複数削除し、所定位置に複数生成します。 リストボックスやテキストボックスのサイズ、位置等変更したら、このエラーに陥る傾向があります。 エラーに陥ったソースをエラーになる前のソースに追加しても、特に問題が起こりません。 エラーメッセージのヘルプを開こうとしても、表示されません。 これは一体なにが原因なのでしょうか? 条件がどうしてもつかめません。 参照設定についても特に参照できていないライブラリはありません。 宜しくお願いします。

  • SortedSetならぬSortedListの良い実装はありませんか

    「SortedSet」の重複を許さない制限を取り払ったクラス、(あえていうならば)「SortedList」なる高速な実装はないでしょうか。 頻繁に挿入と削除を繰り返すので、TreeSetのように木構造が望ましいのですが、自分でB木とか何とか木を実装するのも骨が折れるので、そのようなライブラリや、こうすれば簡単にできるよ!的なテクニックがあれば教えていただければと思います。 今はArrayListよりはLinkedListの方がましかなという理由でこんな感じで実装しています。 interface SortedList <E extends Comparable<E>> {  public void add(E v);  public boolean remove(E v);  public E get(int i); } class SortedLinkedList <E extends Comparable<E>>  extends LinkedList<E>  implements SortedList<E> {  public void add(E v) {   int i=0, size=size();   for(i=0; i<size; i++)    if(v.compareTo( get(i) )<0)     break;   add(i, v);  }  public boolean remove(E v) {   return super.remove(v);  }  public E get(int i) {   return get(i);  } }

    • ベストアンサー
    • Java
  • 画像ファイルをアップロードする前に縦横のサイズを取得

    ローカルに保存されている任意の画像を選択(input type=fileなどで) すると、テキストボックスにその画像の縦横サイズを表示するといった 事をやりたいのですが、Javascriptで実現可能でしょうか? img.src = "ファイルパス"; img.height; img.width; でサイズ取得は可能ですが、ファイルパスを直書きするのではなく、 あくまで、任意の画像を選択して取得したいです。 わかる方がいらっしゃいましたらご教授いただきたいと思います。 よろしくお願いします。

  • iPhoto Libraryのパッケージの内容

    iMac(OSX)を使っています。 iPhotoで読み込んだ写真は「ピクチャ」の中の 「iPhoto Library」に全て保存されていますが、 この「iPhoto Library」を右クリックして 「パッケージの内容を表示」すると、 「Library.iPhoto」と「Library6.iPhoto」といった 2つのフォルダが表示されます。 両方とも中身の写真は同じのようなので、片方を 削除しようかと思いますが、「情報を見る」で それぞれを比べて見ると少し違いがあります。 削除してはいけないような気がします。 「Library.iPhoto」のほうはサイズ22バイトで 「Library6.iPhoto」は12バイトとなっています。 ちなみに、外付HDDにもコピーしてますので、 同様に同じ物が2つ存在しています。 何かの手違いで2つ出来てしまったのなら、 サイズの小さい「Library6.iPhoto」を削除しよう と思いますが大丈夫でしょうか?

    • ベストアンサー
    • Mac
  • Illustrator バイナリの外部ファイル出力

    Illustrator CS5 で JSX を使用して、以下の処理を書こうとしています。 (1) アクティブドキュメントから画像オブジェクトを取得 (2) 外部XMLファイルの要素内に、文字列として画像のバイナリを出力して保存 上記の処理を実現することはできるのでしょうか。 情報をなかなか見つけることができずに困っています。 よろしくお願いいたします。

  • Form オブジェクトの戻り値の設定

    はじめまして。Porome です。 Excel 2003 の VBA を使ってプログラミングをしています。 Form オブジェクトの戻り値の設定方法/受取方法について教えてください。 実装したいプログラムの概要は、以下の通りです。 標準モジュールに記載したサブルーチンから Form オブジェクトを 生成し、Form オブジェクトで入力されたデータを標準モジュール側 で取得する…というものです。 書きたいソースコードのイメージは以下の通りです。 Sub MySub1() dim i_Form As New MyForm1 dim i_Name As String dim i_Age As String dim i_Ret As Integer 'MyForm1OK, MyForm1Cancel が戻ってくるまで、じっと待つ i_Ret = i_Form.Show, vbModal Select Case i_Ret 'MyForm1 で OK ボタンが押された Case MyForm1OK: i_Name = i_Form1.txtName.Value i_Age = i_Form1.txtAge.Value MsgBox(i_Name & " さんの年齢は " & i_Age & " 歳です") 'MyForm1 で Cancel ボタンが押された Case MyForm1Cancel: MsgBox("処理はキャンセルされました") 'MyForm1 では、MyForm1 では、MyForm1OK, MyForm1Cancel 以外は '戻さない仕様とします。 Case Else '何もしない(ここには来てはいけない) End Select End Sub こういったことを実現するためには ・MyForm1 では、 OK ボタンを押したら、MyForm1OK を Show メソッドの戻り値として セットし、処理を MySub1 に戻す Cancel ボタンを押したら、MyForm1Cancel を Show メソッドの戻り値としてセットし、処理を MySub1 に戻す OK ボタン、Cancel ボタン Click 以外の Form イベントでは、MyForm1 での処理が継続され、MySub1 は MyForm1 の戻り値(MyForm1OK/MyForm1Cancel)を、じっと待つ といったことが必要になってくるかと思います。 こういった処理を実装するには、呼び出し元 (MySub1)、呼び出し先 (MyForm1) では、それぞれどのようなコードを記載すればよろしいでしょうか? 説明が下手で申し訳ありませんが、アドバイスをお願いいたします

  • VBAでバイナリーデータを画像として貼り付けたい

    お世話になります。 現在、ExcelからOracleのデータベースを検索して値を選択→シートに張り付けて印刷する、請求書発行のプログラムを作っています。 そこで、実装方法が不明なところがでてきましたので、もしご存知の方は教えてください。 Oracleのデータベースには、Blob型のカラムに請求書発行元会社のロゴデータ(gif形式)が格納されていますが、これをExcelのシートに画像オブジェクトとして貼り付けようとしています。 Oracleのデータベースからの値の取得はできていて、これを画像オブジェクトとしてシートに貼り付ける方法をいろいろ試行錯誤しているのですが、いまだ実現できずにいます。 ※できれば、サンプルのプログラムも記載いただければ幸いです

  • [C言語]freeする領域のサイズは誰が管理?

    趣味でプログラミングの勉強をしています。 C言語で、ふと疑問に思いましたので質問させていただきます。 動的に領域を確保するときにはmalloc()にサイズを与えて、その領域のアドレスを取得します。 で、解放するときにはfree()に領域のアドレスを与えます。 ここで疑問に思ったのですが、領域を開放する場合にはその領域のアドレスとサイズが必要ではないかと思うのですが、free()ではアドレスしか与えませんよね? 誰かがアドレスとサイズが対になった情報を管理する必要があると思うのですが、誰が管理しているのでしょうか? 単純に考えてコンパイルしてできた実行形式のファイル中(あるいは呼ばれる外部ライブラリ?)にその機能があると思うのですが、それで合っていますか? よろしくお願いします。

  • CFileの使い方を教えてください

    環境はWIN98 VC++6.0 MFCです。 見よう見真似で以下のようにしたら、配列のデータがXXX.txtファイルに保存されました。 それじゃってことで、XXX.txtファイルからデータを取り出し、元通りに配列aa[n]に格納するにはどうすれば良いのですか? そう言うことはやれるのですか? void CAbcDlg::OnButton4() {  // TODO: この位置にコントロール通知ハンドラ用のコードを追加してください  CString str;  LPTSTR pszBuf;  long size;   CFile fpw( "XXX.txt", CFile::modeCreate | CFile::modeWrite | CFile::shareExclusive );   for(n=0;n<10;n++){    str=aa[n];    size = str.GetLength();    fpw.Write( &size, sizeof( long ) );    //バッファの確保    pszBuf = str.GetBuffer( size );   //書き込み    fpw.Write( pszBuf, size );   //バッファの開放   str.ReleaseBuffer();  } }