web-dev-qa-db-ja.com

Java継承のサイズコストはいくらですか?

Java.lang.Object、特にJVM実装のオーバーヘッドを経験的に見積もろうとする、さまざまな記事がインターウェブ上にあります。たとえば、一部のJVMでは、裸のObject8バイトと推定 のサイズオーバーヘッドを見てきました。

私が知りたいのは、extends関係の典型的なJVM実装が、クラス階層のすべてのレベルで増分サイズのオーバーヘッドを導入するかどうかです。つまり、Nレベルのサブクラスを持つクラス階層があるとします。クラスインスタンスのメモリ内表現のオーバーヘッドはO(1)またはO(N)ですか?

O(1)隠されたふわふわのもののサイズは、Java Object( vtable、クラスのチェーン)は、継承階層が成長するにつれて成長し、インスタンスごとではなくクラスごとに成長します。JVM実装は、これらのエンティティへの一定サイズのポインタを、すべてのObject

したがって、理論的には、任意のJavaオブジェクトのメモリ内表現に直接付加されるオーバーヘッドは、継承深度Nに対してO(1)である必要があります。誰か知っていますかそれが実際に本当なら?

52
0xbe5077ed

疑わしい場合は、 ソース (まあ、aソースを見てください。標準では義務付けられていないため、各JVMはそれを行う方法を自由に選択できます。内部表現)。そこで、調べてみると、JDK 7-u60のホットスポットJVMの実装内に 次のコメント が見つかりました。

// A Klass is the part of the klassOop that provides:
//  1: language level class object (method dictionary etc.)
//  2: provide vm dispatch behavior for the object
// Both functions are combined into one C++ class. The toplevel class "Klass"
// implements purpose 1 whereas all subclasses provide extra virtual functions
// for purpose 2.

// One reason for the oop/klass dichotomy in the implementation is
// that we don't want a C++ vtbl pointer in every object.  Thus,
// normal oops don't have any virtual functions.  Instead, they
// forward all "virtual" functions to their klass, which does have
// a vtbl and does the C++ dispatch depending on the object's

私の読み方では、これは、この(非常に人気のある)実装では、オブジェクトインスタンスがクラスへのポインタのみを格納することを意味します。 継承チェーンが長いまたは短いクラスのインスタンスごとのコストは事実上0です。ただし、クラス自体はメモリ内のスペースを占有します(ただし、クラスごとに1回のみ)。ディープ継承チェーンの実行時の効率は別の問題です。

22
tucuxi

JVM仕様の状態

Java仮想マシンは、オブジェクトの特定の内部構造を要求しません。

したがって、仕様はそれをどのように行うかを気にしません。 しかし.。

OracleのJava仮想マシンの実装の一部では、クラスインスタンスへの参照は、それ自体がポインタのペアであるハンドルへのポインタです。オブジェクトのメソッドと、オブジェクトのタイプを表すClassオブジェクトへのポインタ、およびオブジェクトデータ用にヒープから割り当てられたメモリへのポインタを含むテーブル。

したがって、一般的なOracle実装では、メソッドの場合はO(1)です。このメソッドテーブルは、クラスごとの メソッド領域 です。

Java仮想マシンには、すべてのJava仮想マシンスレッド間で共有されるメソッド領域があります。メソッド領域は、コンパイルされたコードのストレージ領域に類似しています。従来の言語またはオペレーティングシステムプロセスの「テキスト」セグメントに類似。ランタイム定数プール、フィールドおよびメソッドデータなどのクラスごとの構造、および特別なメソッドを含むメソッドとコンストラクターのコードを格納します(§ 2.9)クラスとインスタンスの初期化およびインターフェイスの初期化で使用されます。

また、 メソッドエントリ について

method_info構造は、インスタンスメソッド、クラスメソッド、インスタンス初期化メソッド(§2.9)、および任意のクラスまたはインターフェイス初期化メソッド(§2.9)を含む、このクラスまたはインターフェイスタイプによって宣言されたすべてのメソッドを表します。 メソッドテーブルには、スーパークラスまたはスーパーインターフェイスから継承されたメソッドを表すアイテムは含まれていません。

23

インスタンスには通常、次のデータが必要ですが、正確に何をするかは実装次第です。

  • クラスとその親クラスのインスタンスフィールド。これは、「オーバーヘッド」という用語に含めることを意味しますしないでください
  • オブジェクトをロックするためのいくつかの手段
  • ガベージコレクターがオブジェクトを再配置する場合、オブジェクトの元のハッシュを記録するための何らかの手段があります(Object.hashCode
  • タイプ情報にアクセスするためのいくつかの手段

あなたの質問で推測するように、「通常の」Java実装では、型情報はインスタンスごとではなくクラスごとに格納されます。「型」の定義の一部は、同じクラスは必ず同じ型情報を持っているので、それを共有しない明確な理由はありません。したがって、クラス階層に依存するのではなく、インスタンスごとのオーバーヘッドが一定であることが期待されます。

つまり、空のクラスまたはインターフェイスをクラスに追加しても、そのインスタンスのサイズが大きくなることはありません。ただし、言語もJVM仕様も実際にこれを保証するとは思わないので、「非正規」Java実装で実行できることについてあまり多くの仮定をしないでください。

余談ですが、私のリストの2番目と3番目のものは、狡猾なトリックを介して組み合わせることができるため、両方が1つのポインターになります。あなたがリンクしている記事は4バイトを取る参照を参照しているので、オブジェクトに対して出てくる8バイトは、型情報への1つのポインターであり、1つのフィールドにはどちらかハッシュコードまたはが含まれます。 =モニターへのポインター、およびおそらくこれらのポインターフィールドの一方または両方の下位2ビットにあるいくつかのフラグ。 Objectは、64ビットJavaでは(予想どおり)大きくなります。

4
Steve Jessop

Objectを拡張するNumberを拡張するDoubleとIntegerには、O(n)の動作がありません。つまり、整数はObjectの3倍のサイズではないので、答えははO(1)です。例: this old SO question

2
user949300

理論的には、任意のJavaオブジェクトのメモリ内表現に直接付加されるオーバーヘッドは、継承深度Nの場合O(1)である必要があります。それは実際には本当ですか?

すべてのレベルにインスタンスメンバーがゼロでない限り、O(1)にすることはできません。すべてのインスタンスメンバーには、インスタンスごとにスペースが必要です。

1
user207421