Javaプログラムで1バイトが占有するメモリ量について、さまざまな意見を聞いたことがあります。
Javaバイト、および ドキュメント は、バイトは8ビットだけですが ここで 実際にはintと同じ量のメモリを消費するため、効率ではなくコードの理解に役立つTypeにすぎないと言われています。
誰もこれを解決できますか?これは実装固有の問題でしょうか?
さて、多くの議論があり、多くのコードはありませんでした:)
ここに簡単なベンチマークがあります。この種のことになると、通常の注意事項があります-JITtingなどによるメモリのテストには奇妙な点がありますが、適切な数であれば、とにかく便利です。それぞれ80のメンバーを持つ2つのタイプがあります-LotsOfBytesは80バイト、LotsOfIntsは80 intです。それらの多くを構築し、GCされていないことを確認し、メモリ使用量を確認します。
class LotsOfBytes
{
byte a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af;
byte b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf;
byte c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, ca, cb, cc, cd, ce, cf;
byte d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, da, db, dc, dd, de, df;
byte e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, ea, eb, ec, ed, ee, ef;
}
class LotsOfInts
{
int a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af;
int b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf;
int c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, ca, cb, cc, cd, ce, cf;
int d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, da, db, dc, dd, de, df;
int e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, ea, eb, ec, ed, ee, ef;
}
public class Test
{
private static final int SIZE = 1000000;
public static void main(String[] args) throws Exception
{
LotsOfBytes[] first = new LotsOfBytes[SIZE];
LotsOfInts[] second = new LotsOfInts[SIZE];
System.gc();
long startMem = getMemory();
for (int i=0; i < SIZE; i++)
{
first[i] = new LotsOfBytes();
}
System.gc();
long endMem = getMemory();
System.out.println ("Size for LotsOfBytes: " + (endMem-startMem));
System.out.println ("Average size: " + ((endMem-startMem) / ((double)SIZE)));
System.gc();
startMem = getMemory();
for (int i=0; i < SIZE; i++)
{
second[i] = new LotsOfInts();
}
System.gc();
endMem = getMemory();
System.out.println ("Size for LotsOfInts: " + (endMem-startMem));
System.out.println ("Average size: " + ((endMem-startMem) / ((double)SIZE)));
// Make sure nothing gets collected
long total = 0;
for (int i=0; i < SIZE; i++)
{
total += first[i].a0 + second[i].a0;
}
System.out.println(total);
}
private static long getMemory()
{
Runtime runtime = Runtime.getRuntime();
return runtime.totalMemory() - runtime.freeMemory();
}
}
私のボックスに出力:
Size for LotsOfBytes: 88811688
Average size: 88.811688
Size for LotsOfInts: 327076360
Average size: 327.07636
0
そのため、明らかにオーバーヘッドがあります-見た目では8バイトですが、どういうわけかLotsOfIntsには7バイトしかありません(私が言ったように、ここには奇妙な点があります)-ポイントは、バイトフィールドがLotsOfBytesに詰め込まれているように見えることです(オーバーヘッドを除去した後)LotsOfIntsの4分の1のメモリのみを取ります。
はい、Javaのバイト変数は実際にはメモリ内の4バイトです。ただし、これは配列には当てはまりません。20バイトのバイト配列のストレージは実際には20バイトだけです。メモリ。
これは、Java Bytecode Languageはintとlongsの2つの整数型のみを知っているため、いずれかの型として内部ですべての数値を処理する必要があります。
ただし、Javaはすべての整数形式の配列を知っています。したがって、短い配列のストレージは、実際にはエントリごとに2バイト、バイト配列のエントリごとに1バイトです。
私が「ストレージ」と言い続ける理由は、配列もJavaのオブジェクトであり、すべてのオブジェクトは、インスタンス変数または配列が必要な場合の配列ストレージ。
Javaは実装またはプラットフォーム固有ではありません(少なくとも プリミティブ型のサイズ に関する限り)。これらのプリミティブ型は、どのプラットフォームを使用していても常に同じであることが保証されています。これは、一部のプリミティブ型がプラットフォーム固有であったCおよびC++とは異なります(そして、改善されたと見なされました)。
基盤となるオペレーティングシステムが一度に4(または64ビットシステムでは8)バイトを処理する方が速いため、JVMはプリミティブバイトを格納するためにより多くのバイトを割り当てることができますが、-128からその中に127。
http://code.google.com/p/memory-measurer/ を使用してテストを行いました。64ビットOracle/Sunを使用していることに注意してくださいJava 6 、参照などの圧縮なし.
各オブジェクトはいくつかのスペースを占有し、JVMはそのオブジェクトのアドレスを知る必要があり、「アドレス」自体は8バイトです。
プリミティブでは、パフォーマンスを向上させるためにプリミティブが64ビットにキャストされているように見えます(もちろんです!)。
byte: 16 bytes,
int: 16 bytes,
long: 24 bytes.
配列あり:
byte[1]: 24 bytes
int[1]: 24 bytes
long[1]: 24 bytes
byte[2]: 24 bytes
int[2]: 24 bytes
long[2]: 32 bytes
byte[4]: 24 bytes
int[4]: 32 bytes
long[4]: 48 bytes
byte[8]: 24 bytes => 8 bytes, "start" address, "end" address => 8 + 8 + 8 bytes
int[8]: 48 bytes => 8 integers (4 bytes each), "start" address, "end" address => 8*4 + 8 + 8 bytes
long[8]: 80 bytes => 8 longs (8 bytes each), "start" address, "end" address => 8x8 + 8 + 8 bytes
そして今、何を推測する...
byte[8]: 24 bytes
byte[1][8]: 48 bytes
byte[64]: 80 bytes
byte[8][8]: 240 bytes
追伸Oracle Java 6、最新かつ最高の64ビット、1.6.0_37、MacOS X
明らかな課題は、バイトと整数で単純なことを行うコードで javap を実行することです。バイトで動作するintパラメーターを予期するバイトコードと、相互に強制変換するために挿入されるバイトコードが表示されます。
ただし、バイトの配列は4バイト値の配列として保存されないため、長さ1024のバイト配列は1kのメモリを使用します(オーバーヘッドを無視します)。
JVMがパディングなどをどのように適用するかに依存します。バイトの配列は(任意の健全なシステムで)要素ごとに1バイトにパックされますが、4バイトフィールドを持つクラスは、タイトにパックされるか、Wordの境界にパディングされる可能性があります-実装に依存します。
あなたが言われたことは正確です。 Javaバイトコード仕様には、4バイトタイプと8バイトタイプのみがあります。
byte、char、int、short、boolean、floatはすべて4バイトで保存されます。
doubleとlongは8バイトで保存されます。
ただし、バイトコードは話の半分にすぎません。実装固有のJVMもあります。 Java変数がバイトとして宣言されたことを判断するためのバイトコードに十分な情報があります。JVM実装者mayバイトのみを使用することにします。ありそうにない。
常にlongを使用し、自分でデータをパックして効率を高めることができます。そうすれば、4バイトすべてを使用することになります。
byte = 8bit = Java Spec。
バイト配列が必要とするメモリの量は、仕様で定義されているnotではなく、複雑なオブジェクトが必要とする量も定義されていません。
Sun JVMについては、ルールを文書化しました。 https://www.sdn.sap.com/irj/sdn/weblogs?blog=/pub/wlg/516
ただ声明を指摘したかった
Java byteには+127以下しか保存できません
本当に正しくありません。
1つのバイトには常に256個の異なる値を格納できるため、0〜255の範囲を「符号なし」バイトのように簡単に設定できます。
これらはすべて、これらの8ビットをどのように処理するかによって異なります。
例:
byte B=(byte)200;//B contains 200
System.out.println((B+256)%256);//Prints 200
System.out.println(B&0xFF);//Prints 200
私のサイト(www.csd.uoc.gr/~andreou)でMonitoringToolsを参照してください
クラスX { バイトb1、b2、b3 ...; } long memoryUsed = MemoryMeasurer.measure(new X() );
(より複雑なオブジェクト/オブジェクトグラフにも使用できます)
Sunの1.6 JDKでは、1バイトが実際に1バイト(古いバージョンでは、メモリの観点からint〜byte)を占めるようです。ただし、古いバージョンでも、byte []はエントリごとに1バイトにパックされていました。
とにかく、重要なのは、上記のJon Skeetのような複雑なテストは必要ないということです。これは推定のみを提供します。オブジェクトのサイズを直接測定できます!
上記のコメントを読んで、私の結論は多くの人にとって驚きになると思われるので(私にとっても驚きです)、繰り返す価値があります:
代わりに、size(byte)== 1 byte(!!)