Javaのブールプリミティブサイズが定義されていないのはなぜですか?


111

Java仮想マシン仕様は、 boolean型のサポートに制限があることを述べているプリミティブ型は。

ブール値の操作専用のJava仮想マシン命令はありません。代わりに、ブール値を操作するJavaプログラミング言語の式は、Java仮想マシンのintデータ型の値を使用するようにコンパイルされます。

上記は、ブール値を操作するときにintデータ型が使用されることを意味します(誤解している可能性があります)が、これは32ビットのメモリ構造です。ブール値が1ビットの情報のみを表すとすると、

  • intの代わりにバイト型またはshort型がブール値のプロキシとして使用されないのはなぜですか?
  • 特定のJVMについて、ブール型を格納するために使用されているメモリの量を正確に調べる最も信頼できる方法は何ですか?

回答:


116

短い答え:はい、ブール値は32ビットエンティティとして操作されますが、ブール値の配列は要素ごとに1バイトを使用します。

より長い答え:JVMは32ビットのスタックセルを使用し、ローカル変数、メソッド引数、および式の値を保持するために使用されます。1セルより小さいプリミティブはパディングされ、32ビットより大きい(プリミティブおよびダブル)プリミティブは2セルを使用します。この手法は、オペコードの数を最小限に抑えますが、いくつかの奇妙な副作用(バイトをマスクする必要性など)があります。

配列に格納されたプリミティブは32ビット未満を使用する場合があり、配列からプリミティブ値をロードおよび格納するためのさまざまなオペコードがあります。ブール値とバイト値はどちらもbaloadbastoreオペコードを使用します。これは、ブール配列が要素ごとに1バイトを取ることを意味します。

インメモリオブジェクトレイアウトに関する限り、これは「プライベート実装」ルールでカバーされ、1ビット、1バイト、または別の投稿者が指摘したように、64ビットのダブルワード境界に配置できます。ほとんどの場合、基盤となるハードウェアの基本的なワードサイズ(32ビットまたは64ビット)が必要です。


ブール値が使用するスペースの量を最小限に抑える限り、ほとんどのアプリケーションでは問題になりません。スタックフレーム(ローカル変数とメソッド引数を保持する)はそれほど大きくありません。大きなスキームでは、オブジェクト内の離散ブール値もそれほど大きくありません。多数のブール値を持つ多数のオブジェクトがある場合は、ゲッターとセッターを介して管理されるビットフィールドを使用できます。ただし、CPU時間にペナルティを支払うことになりますが、これはおそらくメモリのペナルティよりも大きくなります。


boolean / byteクラスメンバーの場合、それらも4バイトであることは本当ですか?クラスインスタンスは全体としてスタックに割り当てられるので、想像できると思いますが、JVMはおそらくブール/バイトメンバーごとに1バイトを使用し、最後に完全なクラスインスタンスに対して4バイトのアライメントを行う必要があります。そうですか?(これを証明するリファレンスがある場合は、共有してください)
dma_k

@dma_k:私の応答で述べたように、クラスインスタンスのレイアウトは実装に依存します。ただし、クラスインスタンスはスタックに格納されず、ヒープに格納されることに注意してください(オブジェクトをスタックからヒープに移動するJDK 7の「エスケープ分析」への参照がいくつかありますが、そうではないようです。 java.sun.com/javase/7/docs/technotes/guides/vm/…を参照)
kdgregory

1
ブール値のパッキングが実際に高速になる場合があります。キャッシュサイズが重要な場合は常に、パックする方が良い場合があります。たとえば、セグメント化された素数のふるいは32 kB(L1キャッシュサイズ)のチャンクで機能し、セグメント化されていないふるいよりもはるかに高速です。チャンク間にはオーバーヘッドがあり、パッキングを使用すると、オーバーヘッドを8分の1に減らすことができます。まだ測定していません。
maaartinus

7

継承階層のどこかにある単一のブール値は、最大8バイトを使用できます。これはパディングが原因です。詳細については、Javaオブジェクトが使用するメモリの量を参照してください

ブール値の消費量の問題に戻りますが、はい、少なくとも1バイトは消費しますが、整列規則により、さらに多く消費する可能性があります。私見boolean []は、エントリごとに1バイトではなく1バイトを消費し、さらに配列のサイズと配列のサイズフィールドのためにいくつかのオーバーヘッドを消費することを知っていると、さらに興味深いです。大きなビットフィールドが役立つグラフアルゴリズムがあり、boolean []を使用する場合、実際に必要なメモリのほぼ8倍(1バイト対1ビット)のメモリが必要であることに注意する必要があります。


とにかくブール値[]をどのように使用しますか?
トーマスユング

boolean []はマスクに使用できます。ただし、便利なメソッドがいくつかあるため、BitSetの方が優れている場合があります。
Michael Munsey、

5

第5版のJava in a Nutshell(O'Reilly)によると、ブールプリミティブ型は1バイトです。ヒープの検査が示していることに基づいて、それは間違っている可能性があります。ほとんどのJVMで、変数に割り当てるバイトが1バイトに満たないという問題があるのでしょうか。


3

ブール値のマッピングは、32ビットCPUを想定して行われました。int値は32ビットなので、1回の操作で処理できます。

これは、Peter NorvigのJava IAQからの解決策です。サイズを測定するために、あまり答えられない質問があります(多少不正確です)。

static Runtime runtime = Runtime.getRuntime();
...
long start, end;
Object obj;
runtime.gc();
start = runtime.freememory();
obj = new Object(); // Or whatever you want to look at
end =  runtime.freememory();
System.out.println("That took " + (start-end) + " bytes.");

この会話はプリミティブに関するものなので、プリミティブはインスタンスのフィールドまたは配列でない限りヒープに格納されないため、これをテストする際には創造的である必要があります。そして、どちらもJavaがスタックに保存する方法をどのように選択するかという質問には答えません。
ジェシー

2

CPUは特定のデータ型の長さで動作します。32ビットCPUの場合、それらは32ビット長であるため、Javaでは「int」と呼ばれます。CPUが処理できるようになる前に、この長さより下または上にあるすべてのものを埋めるか、この長さに分割する必要があります。これにはそれほど時間はかかりませんが、基本的な操作に1つではなく2つのCPUサイクルが必要な場合は、コスト/時間が2倍になります。

この仕様は32ビットCPU専用で、ネイティブデータ型でブール値を処理できます。

ここでは1つしか持てません:速度またはメモリ-SUNは速度を決定しました。


1

ブールは1ビットの情報を表しますが、その「サイズ」は正確に定義されたものではありません、とSun Javaチュートリアルは言います。ブールリテラルには、trueとfalseの2つの値しかありません。詳細については、Javaデータ型を参照してください。


-10

次のような1つの.javaファイルを作成しませんか。

Empty.java

class Empty{
}

そして、このような1つのクラス:

NotEmpty.java

class NotEmpty{
   boolean b;
}

それらを両方ともコンパイルし、.classファイルを16進エディターで比較します。


5
これは完全に別のメトリックであり、メモリ内のプリミティブなブール型のサイズ設定とは無関係です。
Joel、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.