アプリ版:「スタンプのみでお礼する」機能のリリースについて

オブジェクトのサイズを取得するメソッドとして、

 java.lang.instrument.Instrumentation#getObjectSize

があることは教わったのですが、なんと、このメソッドは参照先オブジェクトの
サイズを調べてくれません。
ですから、私の環境では、Listに何のオブジェクトをaddしても、しなくても
24 が返ってくるだけでした。

どなたか、List内のオブジェクトの合計のヒープサイズを求めるライブラリもしくは、
リフレクションとInstrumentation#getObjectSizeを使った合計のヒープサイズを調べる
具体的な実装例をご存知の方はおられませんでしょうか?

( 前の質問は全く無関係な方向に進んでしまっていますので、
  しばらく放置させていただきます。ご了承ください。)

A 回答 (9件)

以前、Listを使ったバッファのようなものを作らなければならなくなったときに、


使用メモリ量を概算で求めなければならくなり、そのときは以下のような方法でそれを求めました。

import java.lang.instrument.*;
import java.util.*;

class agent {

public static Instrumentation Inst;

public static void premain(String aargs, Instrumentation ins) { Inst = ins; };

}

class getaboutobjectsize extends Object {

public getaboutobjectsize() { super(); };

public long GetAboutObjectSize() { return(agent.Inst.getObjectSize(this)); };

}

class StringGAOS extends getaboutobjectsize {

private String Str;

public StringGAOS(String str) { Str = new String(str); };

public long GetAboutObjectSize() {
return(agent.Inst.getObjectSize(this) +
new Integer(Str.length()).longValue() * 2 + agent.Inst.getObjectSize(Str));
};

}

class IntegerGAOS extends getaboutobjectsize {

private Integer Int;

public IntegerGAOS(Integer num) { Int = new Integer(num); };

public long GetAboutObjectSize() {
return(agent.Inst.getObjectSize(this) + agent.Inst.getObjectSize(Int));
};

}

class StrIntGAOS extends getaboutobjectsize {

public IntegerGAOS Int;
public StringGAOS Str;

public StrIntGAOS(String str, int num) {
Int = new IntegerGAOS(num);
Str = new StringGAOS(str);
};

public long GetAboutObjectSize() {
return(agent.Inst.getObjectSize(this) + Str.GetAboutObjectSize() + Int.GetAboutObjectSize());
};

}

class ListGAOS extends AbstractList {

public static final int MaxLineNumber = 100;

private getaboutobjectsize Line[];

public ListGAOS() {
int Int1;
Line = new getaboutobjectsize[MaxLineNumber];
for (Int1 = 0; Int1 < MaxLineNumber; Int1 ++) Line[Int1] = new getaboutobjectsize();
};

public Object get(int index) {return(Line[index]); };

public int size() { return(MaxLineNumber); };

public void SetLine(int index, getaboutobjectsize item) { Line[index] = item; };

public void RemoveLine(int index) { Line[index] = new getaboutobjectsize(); };

public long GetAboutObjectSize() {
long Long1 = 0l;
int Int1;
for (Int1 = 0; Int1 < MaxLineNumber; Int1 ++)
Long1 += Line[Int1].GetAboutObjectSize() + agent.Inst.getObjectSize(Line[Int1]);
return(Long1 + agent.Inst.getObjectSize(this));
};

}

public class sizeoflist {

public static void main(String args[]) {

ListGAOS BufferList = new ListGAOS();

BufferList.SetLine(0, new StringGAOS("0123456789"));
BufferList.SetLine(1, new IntegerGAOS(100));
BufferList.SetLine(2, new StrIntGAOS("abcdefghijklmnopqrstuvwxyz", -1));
System.out.println(Long.toString(BufferList.GetAboutObjectSize()));

BufferList.SetLine(3, new StringGAOS("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
System.out.println(Long.toString(BufferList.GetAboutObjectSize()));

BufferList.RemoveLine(0);
System.out.println(Long.toString(BufferList.GetAboutObjectSize()));

};

}

オブジェクトの大きさやメモリ上でのアドレスを取得することは、
プログラミング言語においての基本的な機能のはずです。
よってこれらの機能は、全てのオブジェクトの基底クラスのObjectクラスに実装されているべきものだと思います。
しかし、これが実装されていない(実行時クラスは取得できるのに)ということは、
Javaの言語仕様的にこれらの行為を『積極的』に『拒絶』していると考えられます。
もしこのような機能を使いたいのであれば、
使う方の『責任』で全て『手動』で実装しなければならないということです。
    • good
    • 0

>テキストエディタの例で言うなら、文字列データそのものはどこにあるのでしょうか?


>イメージエディタなら、そのラスタデータはどこにあるのでしょうか?これを含んでUndoバッファです。

自分が提案する方法は文字列データの場所を変えるということではありません。
もう一度具体的にご説明します。文字入力を表現するクラス

class CharInput{
  int index;
  char data;
  CharInput(int index, char data){
    this.index = index;
    this.data = data;
  }
}

があって、一文字入力したときに

undoBuffer.add(new CharInput(cursorPosition, data));

としてデータを保持しているとしましょう。でもCharInputのサイズを取得するのは厄介ですから、

undoBuffer.add(new byte[]{charInputTypeCode, cursorPosition, (byte)data});

のようにバイト列で保持すればいい、というだけの話です。
単にデータの保持の方法を変えてサイズが判るようにしているだけです。

必ずしもUndoバッファにそれらのデータが含める必要はありません。
やや話がそれているのですが、もしかして一回分のUndoのデータに
その場面のデータを丸ごと記録しようとしていませんか?上記の例で言えば
undoBuffer.add(textbox.getText());
とやるイメージです。これでもUndoは可能ですが、それは大量のメモリを消費しますよ?
どうしてもその場面でのデータを丸ごと保持したいのであれば、
これまでの回答でお答えしたとおり文字列データなりラスタデータなりをシリアライズして
保持するだけですから、前述の方法でやり方はとくに変わりません。

>どこにあるのでしょうか?

ということに関しては、テキストエディタならテキストボックスには保持されているということになりますし、
それがなくてもUndoバッファを辿ればいつでも最新の状態を再び構築できます。

>例えば、エディタのテクストがフォント名を持っているかもしれない(またはnullかも知れない),
>フォント色を持っているかもしれない(〃),バックグラウンドのテクスチャパターンを持ているかも知れない(〃)
>等を考えてみてください。

その場合も自分にはとくに問題があるようにはみえませんが……?
文字の入力や削除を表現するデータに加えて、フォントの変更の操作を表現するUndoデータを持つだけです。
どこが問題なのでしょう?自分の説明がうまく伝わっていないのでしょうか。

>UndoManagerがUndoableEditのオブジェクト構造を予め全てを知っていなくてはいけないよう実装なら、
>オブジェクト指向言語での設計の利点は殆どなくなるでしょう。

前述のとおり、自分がこのトピックで提案した方法では、データの保持の方法が変わるだけなので
「UndoManagerがUndoableEditのオブジェクト構造を予め全てを知っていなくてはいけないよう実装」に
する必要はありませんよ。
また、さらに話がそれるのですが、オブジェクト指向言語の設計の利点は実装の隠蔽だけではないことを指摘しておきます。
    • good
    • 0

>文書全体の2/3を削除したとしましょう。


>この場合、削除した2/3の情報を保持するよりも、
>残った1/3を保持するほうが効率のよいことは簡単にわかります。

(2/3) > (1/3) ということは「オブジェクトのサイズ」を知らなくてもわかりますよね?
そこでなぜさらに「オブジェクトのサイズ」が必要だと考えておられるのかわかりません。
また「残った1/3」はUndoが出来ないアプリケーションだとしても必要な情報ですから、
「削除した2/3の情報」のどちらが効率がよいかという比較をする意味がよくわかりません。
質問者のかたは「削除した2/3の情報」および「残った1/3の情報」とは
具体的にどのような情報を想定していますか?

自分もごく単純で具体的な例で考えてみます。
テキストエディタで文書全体の一部を削除する操作の場合、
操作を表現するのに必要な最小の情報は、
・削除の操作であることをしめす値
・削除された範囲の開始位置
・削除された範囲の終了位置
になりますので、これを単純にシリアライズします。
すべてintにしてしまいました。

ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream stream = new DataOutputStream(buffer);
stream.writeInt(operationType);
stream.writeInt(startPosition);
stream.writeInt(endPosition);
byte[] data = buffer.toByteArray();

ここで data.length は 12 になります。
これでこのデータのサイズは12バイトと判り、これを読み取ればUndo/Redoもできるわけです。
(もし削除した文字列も保持していた場合、Redo/Redoデータはその分サイズが大きくなりますが、
そのかわりにそれまでの操作履歴を辿らなくてもUndoができるので高速化がはかれます)
ここで、
>削除した2/3の情報を保持するよりも、残った1/3を保持するほうが効率のよい
というような選択が生じるようには見えないのですが……?

>Javaでも、プロファイラ等のツールは、オブジェクトのサイズを知っています

そもそも、Javaには強いセキュリティがあるので、
privateなフィールドへのアクセスはリフレクションを持ってしてもできません。
従ってあるオブジェクトがフィールドに持つすべての参照をたどり、
その先にあるオブジェクトのサイズすべてを計測することはできません。
C言語のsizeofでもポインタの先のデータのサイズまでは含めません。

>それをあいまいにしたまま、誰もがUndoバッファを実装ているとはとても思えません。

自分はUndo/Redoのデータをbyte[]で保持していれば、
「参照先オブジェクトのサイズを含む、オブジェクトのサイズ」を計測する方法がなくても、
配列のlengthフィールドを参照するだけでサイズが測れると思うのです。
C言語のようなメモリが丸出しの言語であれば独自のメモリ管理を実装できるでしょう。
それは質問者さんのイメージには近いでしょうけれど、
それは安全に欠けるし上で挙げた方法より特別優れているとは思いません。

そうこう考えているうちに、ほかにふたつばかり荒業が思いつきました。
要はUndo/Redoのデータだけ別のメモリ空間に確保できればいいわけですね。
ひとつはネイティブに独自のメモリ管理が出来る仕組みを作り、
そこにJNIでRedo/Undoのデータを書き込む方法。
もうひとつの方法は、もうひとつVirtual Machineを起動して、そっちのヒープにUndo/Redoのデータを格納してしまうとか……。
どっちもさすがにバカバカしいですけれど。

この回答への補足

>ここで data.length は 12 になります。
>これでこのデータのサイズは12バイトと判り、これを読み取ればUndo/Redoもできるわけです。

具体的に、Undoバッファを実装された経験をお持ちでしょうか?
テキストエディタの例で言うなら、文字列データそのものはどこにあるのでしょうか?
イメージエディタなら、そのラスタデータはどこにあるのでしょうか?これを含んでUndoバッファです。


>「参照先オブジェクトのサイズを含む、オブジェクトのサイズ」を計測する方法がなくても、
>配列のlengthフィールドを参照するだけでサイズが測れると思うのです。

例えば、エディタのテクストがフォント名を持っているかもしれない(またはnullかも知れない),
フォント色を持っているかもしれない(〃),バックグラウンドのテクスチャパターンを持ているかも知れない(〃)
等を考えてみてください。
UndoManagerがUndoableEditのオブジェクト構造を予め全てを知っていなくてはいけないよう実装なら、
オブジェクト指向言語での設計の利点は殆どなくなるでしょう。

補足日時:2008/05/03 21:19
    • good
    • 0

>前の質問は全く無関係な方向に進んでしまっています


申し訳ないです。このトピックにおいては具体的な解決策に限って検討してみます。

>このメソッドは参照先オブジェクトのサイズを調べてくれません
そうですね。参照先のオブジェクトはサイズを測るオブジェクトの「外」にあるからですね。
これらの流れを見た限りでは、そもそも目的は「オブジェクトのサイズの取得」ではなく
「バッファのサイズを指定できる無限Undo」のようですから、
手段のひとつにすぎない「オブジェクトのサイズの取得」に限る必要はないようにみえます。

自分なら、Undo/Redoの情報を持つオブジェクトをオンメモリにシリアライズする方法が思いつきます。
java.io.ByteArrayOutputStreamか何かにUndo/Redo一回分のデータを書き込んでしまいます。
そうすれば一回分のサイズを取得できますから、
これを合計すればUndo/Redoで使っているメモリを概算できます。
そもそも、メモリに乗らなかった分は結局シリアライズしてファイルに保存することになるのですから、
コーディングの手間としてはそれほど余計な負担にならないように思います。

ただし、これはシリアライズ/デシリアライズにかかるコストを無視できる場合に限ります。
すばやくデシリアライズできないとUndoボタンの反応が悪くなりそうなので、
実際に実装して使い勝手を試してみるのがいいでしょう。
また、前後のUndo/Redoデータが同じデータを共有していた場合は、
それを別々にシリアライズするとメモリを余分に必要になるかと思います。
Javaにもとから用意されているシリアライズ機構を利用すればコーディングの手間はある程度抑えられますが、
Undo/Redoに使うにはかなり重いですし、オブジェクトをそのままヒープに持っておくのに比べて
メモリ効率がいいかはわかりませんので、自前でシリアライズを書くのも考えたほうがよさそうです。

場合によっては、最初からどんどんファイルに書いていってしまうのもいいかも知れません。
Undo/Redoをするたびにファイルアクセスが発生するわけですが、
アプリケーションの種類によってはそれでも十分だったりしますし、使用メモリが少なくなりますから。

というわけで、Undo/Redoのオブジェクトをオンメモリでシリアライズする方法、
検討してみてはいかがでしょうか。

参考URL:http://java.sun.com/javase/ja/6/docs/ja/technote …

この回答への補足

ご回答ありがとうございます。

しかしながら、問題は直列化の前にあります。

たとえば簡単な例として、テキストエディタを考えて見ます。
文書全体の2/3を削除したとしましょう。
この場合、削除した2/3の情報を保持するよりも、
残った1/3を保持するほうが効率のよいことは簡単にわかります。
ところが、オブジェクトのサイズがわからないと、これが判断できません。

Javaでも、プロファイラ等のツールは、オブジェクトのサイズを知っていますし、
それをあいまいにしたまま、誰もがUndoバッファを実装ているとは
とても思えません。

どなたか、実用的・具体的な実装例をご存じないでしょうか?

補足日時:2008/04/28 09:36
    • good
    • 0

> 無限Undoが必要なアプリケーションの


> Undoバッファ
コンピュータは有限マシンなので無限はありえませんが、基本的にはディスクアクセスによりオンメモリのデータを入れ替えていきます(Undoバッファの全体100%が常時オンメモリにある必要はない)。オンメモリのデータは、ユーザの操作に伴って徐々に肥大していきますから、「オブジェクトのヒープサイズの取得」のスレッドで述べたような技法によりメモリ使用状況をチェックし、そろそろヤバイ状況のときには比較的古い部分を解放(すでにディスク上にあるものなら単純に解放、そうでなければディスクに書き込んでから解放)して、JVMの空きメモリを増やしてやります。

でも実際には、メモリ数ギガバイトの今日の時代では(*)、運用時にディスクアクセスをまったくしない(完全オンメモリ)のデータベースも登場しているぐらいですから、Undoバッファをオブジェクトの参照のLinkedList等とするなら、必要に応じて画像データだけをディスクから読み込む方式で、十分な実用性を実現できると思います(Undo情報そのものは完全に100%オンメモリにできる)。PhotoShopやGIMPのような画像アプリケーションは、すでにその方式のようです(ソースを見てませんが、使った感じとして)。
(*:JVMの-Xmxも、今では2Gb~4Gbを指定可能。)
    • good
    • 0

> 無限Undoが必要なアプリケーションの


> Undoバッファ
コンピュータは有限マシンなので無限はありえませんが、基本的にはディスクアクセスによりオンメモリのデータを入れ替えていきます(Undoバッファの全体100%が常時オンメモリにある必要はない)。オンメモリのデータがUndoバッファの先頭部分だったときは、徐々に肥大していきますから、「オブジェクトのヒープサイズの取得」のスレッドで述べたような技法によりメモリ使用状況をチェックし、そろそろヤバイ状況のときには比較的古い部分を解放(すでにディスク上にあるものなら単純に解放、そうでなければディスクに書き込んでから解放)して、JVMの空きメモリを増やしてやります。

でも実際には、メモリ数ギガバイトの今日の時代では(*)、運用時にディスクアクセスをまったくしない(完全オンメモリ)のデータベースも登場しているぐらいですから、Undoバッファをオブジェクトの参照のLinkedList等とするなら、必要に応じて画像データだけをディスクから読み込む方式で、十分な実用性を実現できると思います(Undo情報そのものは完全に100%オンメモリにできる)。PhotoShopやGIMPのような画像アプリケーションは、すでにその方式のようです(ソースを見てませんが、使った感じとして)。
(*:JVMの-Xmxも、今では2Gb~4Gbを指定可能。)

この回答への補足

>コンピュータは有限マシンなので無限はありえませんが、
無限Undoの意味すらわからない人の回答らしく支離滅裂です。

>メモリ使用状況をチェックし、そろそろヤバイ状況のときには比較的古い部分を解放
>メモリ数ギガバイトの今日の時代では
”そろそろヤバイ”って何ですか?
メモリが何GBあろうとも、有限である限り原理的には全く同じです。
freeMemoryとオブジェクトサイズを比較するからこそ、”そろそろヤバイ”か
どうかが判断できるのでしょう。

>PhotoShopやGIMPのような画像アプリケーションは、
>すでにその方式のようです(ソースを見てませんが、使った感じとして)。
適当な思いつきの回答は必要としていません。
一般的なアプリは当然Undoバッファのサイズを制御してますから。

もう、あなたからの回答に対する返信は二度としません。

補足日時:2008/04/18 12:16
    • good
    • 0

>(Runtime#totalMemory()と、Runtime#freeMemory()を


> 利用している方法です。)
ですから、筆者(Vlad Rob氏)が類似記事(これはたいへんな労作!):
http://www.javaworld.com/javaworld/javaqa/2003-1 …
で断っているように、利用状況に制約があり(hirusagari説と同様)、かつ、オブジェクトのサイズの近似値としても限界があります。

Javaにsizeof()がないのは、Javaではプログラマがメモリアロケーションを物理的に制御できないため。だから、オブジェクトのアロケーションの直後にfreeMemory()ががくっと減れば、それをサイズの見当にするぐらいで十分です。オブジェクトのサイズにこだわるのは、Javaでポインタを使いたい!とこだわるのに似ている。つまり、せっかくの高レベル言語(プログラマにいろいろ楽をさせてくれる)を、Cなみの低レベル言語として使おうとしている。無駄で、無意味なことです。

この回答への補足

>無駄で、無意味なことです。

では、CADソフトのように無限Undoが必要なアプリケーションの
Undoバッファは、Javaでは具体的にどのようにして実装するんですか?

補足日時:2008/04/17 22:04
    • good
    • 0

参考URLの方法で調査してみてはどうでしょうか?



(Runtime#totalMemory()と、Runtime#freeMemory()を
 利用している方法です。)

参考URL:http://www.javaworld.com/javaworld/javatips/jw-j …
    • good
    • 0

Javaには、オブジェクトのサイズを知る方法は提供されていません。

それは、昔からのJava常識の一つです。それにまた、プログラマにとって、オブジェクトのサイズを知る必要性もまったくありません。ランタイムのメモリチェック~メモリ管理のためには、Runtime.freeMemory()を使うのが標準的な方法です。このメソッドが返す値も、概略の値ですが。

> 前の質問は全く無関係な方向に進んでしまっていますので、
とほほ。とんでもないご感想を!。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!