Javaプリミティブとオブジェクト
1.概要
このチュートリアルでは、Javaプリミティブ型とそれに対応するものの使用の長所と短所を示します。
2. Java型システム
Javaには、
int
、
boolean
などのプリミティブと、
Integer、
Boolean
などの参照型から構成される2重型システムがあります。すべてのプリミティブ型は参照型に対応します。
すべてのオブジェクトは、対応するプリミティブ型の単一の値を含みます。
ラッパークラスは不変
(オブジェクトが構築された後はその状態が変化しないように)** そして最終的なクラス(継承しないように)になります。
実際には、実際の型が宣言された型と異なる場合、Javaはプリミティブ型と参照型の間の変換を実行します。
Integer j = 1; //autoboxing
int i = new Integer(1);//unboxing
プリミティブ型を参照型に変換するプロセスはオートボクシングと呼ばれ、反対のプロセスはアンボックス化と呼ばれます。
3.長所と短所
どのオブジェクトを使用するかは、どのようなアプリケーションパフォーマンスを達成しようとしているか、使用可能なメモリの量、使用可能なメモリの量、および処理するデフォルト値によって異なります。
それらのどれにも直面しなければ、私達はそれらを知る価値があるけれども私達はこれらの考察を無視するかもしれない。
3.1. 単一アイテムのメモリ使用量
参考までに、https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html[基本型変数]は、メモリに次のような影響を与えます。
-
ブール – 1ビット
-
バイト – 8ビット
-
short、char – 16ビット
-
int、float – 32ビット
-
long、double – 64ビット
実際には、これらの値は仮想マシンの実装によって異なります。たとえば、OracleのVMでは、ブール型はint値0および1にマッピングされるため、ここで説明するように32ビットかかります。https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.3.4[プリミティブ型と値]
これらの型の変数はスタック内にあるため、高速にアクセスされます。
詳しくは、Javaメモリモデルに関するhttps://www.baeldung.com/java-stack-heap[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)
オブジェクトの内部構造を取得するには、http://openjdk.java.net/projects/code-tools/jol/[Java Object Layout]ツールを使用します(別のhttps://www.baeldung.com/javaを参照)。 -size-of-object[チュートリアル]オブジェクトのサイズを取得する方法について。
このJVM上の参照型の単一インスタンスは、192ビットを占有する
Long
と
Double
を除いて、128ビットを占有します。
-
ブール – 128ビット
-
バイト – 128ビット
-
短い、文字 – 128ビット
-
整数、浮動小数点 – 128ビット
-
ロング、ダブル – 192ビット
1つの
Integer
変数が4つの
int
変数と同じくらいのスペースを占有するのに対し、
Boolean
タイプの単一変数は128のプリミティブ変数と同じくらいのスペースを占有することがわかります。
3.2. アレイのメモリ使用量
検討中のタイプの配列がどれだけのメモリを占有しているかを比較すると、状況はさらに興味深いものになります。
すべての型に対してさまざまな数の要素を持つ配列を作成すると、プロットが得られます。
リンク:/uploads/plot-memory-bits.gif[]
これは、メモリ
m(s)
が配列の要素数sにどのように依存するかに関して、型が4つのファミリに分類されることを示しています。
-
長、倍: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
より多くのメモリを消費します。
プリミティブ型の単一要素配列は、対応する参照型よりも、ほとんど常に高価です(longとdoubleを除く)。
3.3. パフォーマンス
Javaコードのパフォーマンスは非常に微妙な問題です。コードが実行されるハードウェア、特定の最適化を実行する可能性のあるコンパイラ、仮想マシンの状態、他のプロセスのアクティビティによって大きく異なります。オペレーティング・システム。
すでに説明したように、プリミティブ型はスタック内に存在し、参照型はヒープ内に存在します。これは、オブジェクトへのアクセス速度を決定する主な要因です。
プリミティブ型に対する操作がラッパークラスに対する操作よりどれだけ速いかを示すために、最後のものを除いてすべての要素が等しい500万要素配列を作成しましょう。それからその要素に対して検索を実行します。
while (!pivot.equals(elements[index])) {
index++;
}
配列にプリミティブ型の変数が含まれている場合と、参照型のオブジェクトが含まれている場合について、この操作のパフォーマンスを比較します。
よく知られているhttp://openjdk.java.net/projects/code-tools/jmh/[JMH]ベンチマークツールを使用します(https://www.baeldung.com/java-microbenchmark-harness[tutorial]を参照)。そしてそれをどのように使うかについて、そして検索操作の結果はこのチャートで要約することができます:
リンク:/uploads/plot-benchmark-primitive-wrapper-3.gif[]
このような単純な操作であっても、ラッパークラスに対して操作を実行するにはさらに時間がかかることがわかります。
加算、乗算、除算などのより複雑な演算の場合は、速度の差が急増する可能性があります。
3.4. デフォルト値
プリミティブ型のデフォルト値は、数値型では
0
(対応する表現では
0
、
0.0d
etc)、ブール型では
false
、文字型では
\ u0000
です。ラッパークラスの場合、デフォルト値は
null
です。
つまり、プリミティブ型はドメインからのみ値を取得できますが、参照型は何らかの意味でドメインに属さない値(
null
)を取得する可能性があります。
変数を未初期化のままにしておくことはお勧めできませんが、作成後に値を割り当てることがあります。
そのような状況で、プリミティブ型変数がその型のデフォルト値と等しい値を持つとき、その変数が本当に初期化されたかどうかを調べるべきです。
null
値は変数が初期化されていないことを示す明白な指示であるため、ラッパークラス変数にはそのような問題はありません。
4.使い方
これまで見てきたように、プリミティブ型はずっと速く、必要なメモリもずっと少なくて済みます。したがって、それらを使用したいと思うかもしれません。
一方、現在のJava言語仕様では、パラメータ化された型(総称)、Javaコレクション、またはReflection APIの中でプリミティブ型を使用することはできません。
私たちのアプリケーションが多数の要素を持つコレクションを必要とするとき、上のプロットに示されるように、できるだけ「経済的」なタイプの配列を使うことを考えるべきです。
5.まとめ
このチュートリアルでは、Javaのオブジェクトは、それらの原始的な類似物よりも遅く、メモリへの影響が大きいことを示しました。
いつものように、コードスニペットは私たちのサイトにあります。
リポジトリ
GitHubで。