概要
このチュートリアルでは、Javaプリミティブ型とそのラップされた型を使用することの長所と短所を示します。Javaには、int、booleanなどのプリミティブと、Integer、Booleanなどの参照型からなる2つの型システムがあります。 すべてのプリミティブ型は参照型に対応します。
すべてのオブジェクトには、対応するプリミティブ型の単一の値が含まれています。 ラッパークラスは不変であり(オブジェクトが構築されると状態が変更できないように)、最終的なものです(それらから継承できないように)。
内部では、Javaは、実際の型が宣言された型と異なる場合、プリミティブ型と参照型の間の変換を実行します。
Integer j = 1; // autoboxingint i = new Integer(1); // unboxing
プリミティブ型を参照型に変換するプロセスはautoboxingと呼ばれ、反対のプロセスはunboxingと呼ばれます。
長所と短所
使用するオブジェクトの決定は、達成しようとするアプリケーションのパフォーマンス、使用可能なメモリの量、使用可能なメモリ
それらのどれにも直面しなければ、これらの考慮事項を無視するかもしれませんが、それらを知る価値があります。3.1.
3.1. 単一項目のメモリフットプリント
ちょうど参照のために、プリミティブ型変数は、メモリ上の次の影響を持っています:
- boolean–1ビット
- byte–8ビット
- short、char–16ビット
- int、float–32ビット
- long、double–64ビット
実際には、これらの値は仮想マシンの実装によって異なります。 たとえば、OracleのVMでは、ブール型はint値0および1にマップされるため、ここで説明するように32ビットを必要とします(プリミティブ型と値)。
これらの型の変数はスタック内に存在するため、高速にアクセスできます。 詳細については、Javaメモリモデルに関するチュートリアルをお勧めします。
参照型はオブジェクトであり、ヒープ上に存在し、アクセスが比較的遅いです。 彼らは彼らの原始的な対応に関して一定のオーバーヘッドを持っています。
オーバーヘッドの具体的な値は、一般的にJVM固有です。 ここでは、次のパラメータを持つ64ビット仮想マシンの結果を示します。
java 10.0.1 2018-04-17Java(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オブジェク
このJVM上の参照型の単一のインスタンスが128ビットを占有するLongとDoubleを除いて192ビットを占有することが判明しました。
- Boolean–128ビット
- Byte–128ビット
- Short,Character–128ビット
- Integer,Float–128ビット
- Long,Double–192ビット
boolean型の単一の変数は128個のプリミティブ変数と同じくらいのスペースを占有しますが、1つの整数変数は4つのInt変数と同じくらいのスペースを占有3.2.
3.2. 配列のメモリフットプリント
我々は検討中の型の配列を占めるメモリの量を比較すると、状況はより興味深いものになります。
すべての型に対してさまざまな数の要素を持つ配列を作成すると、プロットが得られます。
これは、メモリm(s)が配列の要素sの数にどのように依存するかに関して、型が四つのファミリにグループ化されていることを示しています。
- long,double:m(s)=128+64s
- short,char:m(s)=128+64s
- short,char:m(s)=128+64s
- short,char:m(s)=128+64s
- short,char:m(s)=128+64s
- short,char:m(s)=128+64s
- 128+64
- byte,boolean:m(s)=128+64
- 残り: m(s)=128+64
ここで、角括弧は標準天井関数を示します。驚くべきことに、プリミティブ型longおよびdoubleの配列は、ラッパークラスLongおよびDoubleよりも多くのメモリを消費します。
プリミティブ型の単一要素配列は、対応する参照型よりもほとんど常に高価であることがわかります(longとdoubleを除く)。
3.3. パフォーマンス
Javaコードのパフォーマンスは非常に微妙な問題であり、コードが実行されるハードウェア、特定の最適化を実行する可能性のあるコンパイ
既に述べたように、プリミティブ型はスタックにあり、参照型はヒープにあります。 これは、オブジェクトがアクセスされる速度を決定する支配的な要因です。
プリミティブ型の操作がラッパークラスの操作よりもどれだけ高速であるかを示すために、最後の要素を除いてすべての要素が等しい五百万要素配列を作成しましょう。次に、その要素の検索を実行します。
while (!pivot.equals(elements)) { index++;}
配列にプリミティブ型の変数が含まれている場合と、参照型のオブジェクトが含まれている場合について、この操作のパフォーマンスを比較します。
よく知られているJMHベンチマークツールを使用しています(使用方法についてはチュートリアルを参照してください)。lookup操作の結果は次の表に要約できます。
このような単純な操作であっても、ラッパークラスの操作を実行するのに時間がかかることがわかります。合計、乗算、除算などのより複雑な操作の場合、速度の違いが急騰する可能性があります。
3.4.
3.4. デフォルト値
プリミティブ型のデフォルト値は0です(対応する表現では、0,0です。0dなど)数値型の場合はfalse、ブール型の場合はfalse、char型の場合は\u0000。 ラッパークラスの場合、デフォルト値はnullです。
これは、プリミティブ型がドメインからのみ値を取得できることを意味し、参照型はある意味でドメインに属さない値(null)を取得する可能性があ
変数を初期化しないままにすることは良い習慣とはみなされませんが、作成後に値を割り当てることがあります。
このような状況では、プリミティブ型変数がその型のデフォルト値と等しい値を持つ場合、変数が実際に初期化されているかどうかを調べる必null値は変数が初期化されていないことを示す非常に明白な兆候であるため、ラッパークラス変数にはそのような問題はありません。これまで見てきたように、プリミティブ型ははるかに高速で、必要なメモリははるかに少なくなります。 したがって、私たちはそれらを使用することを好むかもしれません。一方、現在のJava言語仕様では、パラメータ化された型(ジェネリック)、JavaコレクションまたはReflection APIでプリミティブ型の使用は許可されていません。
アプリケーションが多数の要素を持つコレクションを必要とする場合は、上記のプロットに示されているように、可能な限り”経済的な”型の配列
結論
それこのチュートリアルでは、Javaのオブジェクトが遅く、プリミティブな類似体よりもメモリへの影響が大きいことを示しました。
いつものように、コードスニペットはGitHubのリポジトリにあります。>>>コースをチェックしてください