Javaプリミティブとオブジェクト
1. 概要
このチュートリアルでは、Javaプリミティブ型とそれに対応するラップを使用する場合の長所と短所を示します。
2. Java型システム
Javaには、int、booleanなどのプリミティブとInteger,Booleanなどの参照型で構成される2つの型システムがあります。 すべてのプリミティブ型は参照型に対応しています。
すべてのオブジェクトには、対応するプリミティブ型の単一の値が含まれます。 wrapper classes are immutable(オブジェクトが構築されると状態が変更されないようにするため)およびfinal(オブジェクトから継承できないようにするため)。
実際には、実際の型が宣言された型と異なる場合、Javaはプリミティブ型と参照型の間の変換を実行します。
Integer j = 1; // autoboxing
int i = new Integer(1); // unboxing
プリミティブ型を参照型に変換するプロセスはオートボクシングと呼ばれ、反対のプロセスはアンボクシングと呼ばれます。
3. 長所と短所
使用するオブジェクトの決定は、達成しようとするアプリケーションのパフォーマンス、使用可能なメモリの量、使用可能なメモリの量、および処理するデフォルト値に基づきます。
これらのいずれにも直面しない場合は、知っておく価値はありますが、これらの考慮事項を無視する可能性があります。
3.1. 単一アイテムのメモリフットプリント
参考までに、primitive type variablesはメモリに次の影響を与えます。
-
ブール-1ビット
-
バイト– 8ビット
-
short、char – 16ビット
-
int、float – 32ビット
-
ロング、ダブル– 64ビット
実際には、これらの値は仮想マシンの実装によって異なる場合があります。 たとえば、OracleのVMでは、ブール型はint値0および1にマップされるため、ここで説明するように32ビットが必要です:Primitive Types and Values。
これらのタイプの変数はスタック内に存在するため、高速でアクセスされます。 詳細については、Javaメモリモデルのtutorialをお勧めします。
参照型はオブジェクトであり、ヒープ上に存在し、アクセスが比較的遅くなります。 それらには、原始的な対応物に関する特定のオーバーヘッドがあります。
オーバーヘッドの具体的な値は、一般的にJVM固有です。 ここでは、これらのパラメーターを使用した64ビット仮想マシンの結果を示します。
java 10.0.1 2018-04-17
Java(TM) SE Runtime Environment 18.3 (build 10.0.1+10)
Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10.0.1+10, mixed mode)
オブジェクトの内部構造を取得するには、Java Object Layoutツールを使用できます(オブジェクトのサイズを取得する方法については、別のtutorialを参照してください)。
このJVM上の参照型の単一インスタンスは、192ビットを占めるLongとDoubleを除いて、128ビットを占めることがわかります。
-
ブール-128ビット
-
バイト-128ビット
-
Short、Character – 128ビット
-
整数、浮動小数点数– 128ビット
-
ロング、ダブル– 192ビット
Booleanタイプの単一の変数が128個のプリミティブ変数と同じくらいのスペースを占めるのに対し、1つのInteger変数は4つのintタイプと同じくらいのスペースを占めることがわかります。
3.2. アレイのメモリフットプリント
検討中の型の配列を占めるメモリの量を比較すると、状況はさらに興味深いものになります。
タイプごとにさまざまな数の要素を持つ配列を作成すると、プロットが得られます。
これは、メモリm(s)が配列の要素数にどのように依存するかに関して、型が4つのファミリにグループ化されていることを示しています。
-
long、double:m(s)= 128 + 64 s
-
short、char:m(s)= 128 + 64 [s / 4]
-
バイト、ブール値:m(s)= 128 + 64 [s / 8]
-
残り:m(s)= 128 + 64 [s / 2]
ここで、角括弧は標準の天井関数を示します。
驚いたことに、longおよびdoubleのプリミティブ型の配列は、ラッパークラスLongおよびDoubleよりも多くのメモリを消費します。
そのsingle-element arrays of primitive types are almost always more expensive (except for long and double) than the corresponding reference typeのいずれかを見ることができます。
3.3. パフォーマンス
Javaコードのパフォーマンスは非常に微妙な問題であり、コードを実行するハードウェア、特定の最適化を実行する可能性のあるコンパイラ、仮想マシンの状態、他のプロセスのアクティビティに大きく依存しますオペレーティング・システム。
すでに述べたように、プリミティブ型はスタックに存在し、参照型はヒープに存在します。 これは、オブジェクトへのアクセス速度を決定する支配的な要因です。
プリミティブ型の操作がラッパークラスの操作よりもどれだけ速いかを示すために、最後の要素を除いてすべての要素が等しい500万の要素配列を作成しましょう。次に、その要素のルックアップを実行します。
while (!pivot.equals(elements[index])) {
index++;
}
配列にプリミティブ型の変数が含まれる場合と、参照型のオブジェクトが含まれる場合のこの操作のパフォーマンスを比較します。
このような単純な操作でも、ラッパークラスの操作を実行するのに時間がかかることがわかります。
加算、乗算、除算などのより複雑な演算の場合、速度の差が急増する可能性があります。
3.4. デフォルト値
プリミティブ型のデフォルト値は0である(対応する表現では、i 数値型の場合は0、0.0dなど)、ブール型の場合はfalse、文字型の場合は