"From JavaコードからJavaヒープ:アプリケーションのメモリ使用量を理解する" 、つまり、char[]
の代わりにString
を使用すると、
単一の文字の最大オーバーヘッドは24:1です!
しかし、ここでどのオーバーヘッドが参照されているのか理解できません。誰か助けてもらえますか?
出典:
この図は、JDK 6-32ビットに関連しています。
char[]
配列の領域へのポインタとして実装されたJava-7より前の世界の文字列では:
// "8 (4)" reads "8 bytes for x64, 4 bytes for x32"
class String{ //8 (4) house keeping + 8 (4) class pointer
char[] buf; //12 (8) bytes + 2 bytes per char -> 24 (16) aligned
int offset; //4 bytes -> three int
int length; //4 bytes -> fields align to
int hash; //4 bytes -> 16 (12) bytes
}
だから私は数えました:
36 bytes per new String("a") for JDK 6 x32 <-- the overhead from the article
56 bytes per new String("a") for JDK 6 x64.
比較すると、JDK 7+ではString
はchar[]
バッファーとhash
フィールドのみを保持するクラスです。
class String{ //8 (4) + 8 (4) bytes -> 16 (8) aligned
char[] buf; //12 (8) bytes + 2 bytes per char -> 24 (16) aligned
int hash; //4 bytes -> 8 (4) aligned
}
っていうことは:
28 bytes per String for JDK 7 x32
48 bytes per String for JDK 7 x64.
[〜#〜]更新[〜#〜]
3.75:1
の比率については、以下の@Andreyの説明を参照してください。弦の長さが長くなると、この比率は1に下がります。
役立つリンク:
JVMでは、文字変数は単一の16ビットメモリ割り当てに格納され、そのJava変数に変更すると、同じメモリの場所が上書きされます。これにより、文字変数の作成または更新が非常に高速になり、メモリが増加します。安価ですが、文字列で使用される静的割り当てと比較して、JVMのオーバーヘッドが増加します。
JVMは、Java Stringsを可変サイズのメモリ空間(基本的には配列)に格納します。これは、Stringオブジェクトの場合、文字列とまったく同じサイズ(文字列終了文字の場合は1)です。が作成されるか、最初に値が割り当てられます。したがって、初期値「HELP!」のオブジェクトには、96ビットのストレージ(6文字、各サイズは16ビット)が割り当てられます。この値は不変と見なされ、JVMがインライン参照できるようにしますその変数に対して、静的な文字列の割り当てを非常に高速で非常にコンパクトにし、JVMの観点からは非常に効率的です。
ソース記事で参照されている番号について説明してみましょう。
この記事では、通常、クラス、フラグ、ロックで構成されるオブジェクトメタデータについて説明します。
クラスとロックはオブジェクトヘッダーに格納され、32ビットVMでは8バイトを使用します。オブジェクトヘッダーにフラグ情報があるJVM実装に関する情報は見つかりませんでした。これは、これが外部のどこかに格納されるようになる可能性があります(たとえば、オブジェクトへの参照をカウントするガベージコレクターなど)。
したがって、記事が、オブジェクトに関するメタ情報を格納するために12バイトのメモリを使用するいくつかのx32 AbstractJVMについて話していると仮定しましょう。
次に、_char[]
_の場合:
2 * (4 - (length + 2) % 4)
)_Java.lang.String
_の場合:
それでは、_"MyString"
_をString
オブジェクトとして格納するために必要なメモリの量を数えます。
_12 + 16 + (12 + 4 + 2 * "MyString".length + 2 * ("MyString".length % 2)) = 60 bytes.
_
反対側から、データのみを格納するには(データ型、長さなどの情報なしで)必要なことがわかります。
_2 * "MyString".length = 16 bytes
_
オーバーヘッドは_60 / 16 = 3.75
_です
同様に、単一の文字配列の場合、「最大オーバーヘッド」が得られます。
_12 + 16 + (12 + 4 + 2 * "a".length + 2 * ("a".length % 2)) = 48 bytes
2 * "a".length = 2 bytes
48 / 2 = 24
_
記事の作成者のロジックに従って、空の文字列を格納すると、最終的に値無限の最大オーバーヘッドが達成されます:)。
私はそれを取得することができない古いstackoverflowの答えから読んでいました。 OracleのJDKでは、文字列には4つのインスタンスレベルのフィールドがあります。
A character array
An integral offset
An integral character count
An integral hash value
つまり、各Stringは、追加のオブジェクト参照(String自体)と、文字配列自体に加えて3つの整数を導入します。 (オフセットと文字数は、String#substring()メソッドを介して生成されたStringインスタンス間で文字配列を共有できるようにするためのものです。これは、他の一部のJavaライブラリ実装者が避けた設計の選択です。)追加のストレージコストに加えて、文字列を文字配列で保護する境界チェックは言うまでもなく、アクセスインダイレクションのレベルがもう1つあります。
基本的な文字配列のみを割り当てて使用することで問題が解決できる場合は、そこに保存するスペースがあります。 Javaでも、そうするのは慣用的ではありませんが、選択を正当化するために、できれば違いをプロファイルしたことからの証拠について言及することにより、慎重なコメントが必要になります。