Размер байта в памяти - Java
Я слышал неоднозначные мнения относительно объема памяти, которую занимает байт в Java-программе.
Я знаю, что вы можете хранить не более +127 в байте Java, и в документации сказано, что байт составляет всего 8 бит, но здесь мне сказали, что он на самом деле занимает тот же объем памяти, что и int, и, следовательно, просто Тип, который помогает в понимании кода, а не эффективности.
Может кто-нибудь прояснить это, и будет ли это специфической проблемой реализации?
13 ответов
Хорошо, было много дискуссий и не так много кода:)
Вот быстрый тест. Когда дело доходит до такого рода вещей, у него есть обычные предостережения - у тестирования памяти есть странности из-за JITting и т. Д., Но с достаточно большими числами это полезно в любом случае. Он имеет два типа, каждый из которых имеет 80 членов - LotsOfBytes имеет 80 байтов, LotsOfInts имеет 80 байтов. Мы создаем много из них, убедитесь, что они не GC'd, и проверяем использование памяти:
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 байтов по внешнему виду, хотя почему-то только 7 для LotsOfInts (как я уже говорил, здесь есть странности) - но дело в том, что байтовые поля, по-видимому, упакованы для LotsOfBytes так, что занимает (после удаления служебных данных) только четверть памяти, чем LotsOfInts.
Да, переменная байта на самом деле 4 байта в памяти. Однако это не относится к массивам. Массив из 20 байтов на самом деле занимает всего 20 байтов в памяти. Это потому, что язык Java Bytecode Language знает только целые и длинные числа как числовые типы (поэтому он должен обрабатывать все числа как оба типа, 4 байта или 8 байтов), но он знает массивы с каждым возможным размером числа (поэтому короткие массивы находятся в фактически два байта на запись и байтовые массивы фактически являются одним байтом на запись).
Java никогда не зависит от реализации или платформы (по крайней мере, в том, что касается размеров примитивов). Эти примитивные типы всегда гарантированно остаются неизменными независимо от того, на какой платформе вы находитесь. Это отличается от (и считалось улучшением) C и C++, где некоторые из примитивных типов зависели от платформы.
Поскольку базовая операционная система быстрее обращается к четырем (или восьми, в 64-битной системе) байтам одновременно, 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
PS Oracle Java 6, последняя и лучшая, 64-битная, 1.6.0_37, MacOS X
Показательным упражнением является запуск javap для некоторого кода, который делает простые вещи с байтами и целыми числами. Вы увидите байт-коды, которые ожидают, что параметры int работают с байтами, и байт-коды, вставляемые для перехода от одного к другому.
Обратите внимание, что массивы байтов не хранятся как массивы 4-байтовых значений, поэтому байтовый массив длиной 1024 будет использовать 1 КБ памяти (игнорируя любые издержки).
Это зависит от того, как JVM применяет заполнение и т. Д. Массив байтов (в любой разумной системе) будет упакован в 1 байт на элемент, но класс с четырьмя байтовыми полями может быть либо плотно упакован, либо заполнен по границам слов - это зависит от реализации.
byte = 8bit = один байт, определенный Спецификацией Java.
сколько памяти требуется байтовому массиву, не определяется спецификацией и не определяется количеством сложных объектов.
Для Sun JVM я задокументировал правила: https://www.sdn.sap.com/irj/sdn/weblogs?blog=/pub/wlg/5163
То, что вам сказали, совершенно верно. Спецификация байтового кода Java имеет только 4-байтовые типы и 8-байтовые типы.
byte, char, int, short, boolean, float хранятся в 4 байтах каждый.
double и long хранятся в 8 байтах.
Однако байт-код - это только половина истории. Есть также JVM, которая зависит от реализации. В байт-коде Java достаточно информации, чтобы определить, что переменная была объявлена как байт. Разработчик JVM может решить использовать только байт, хотя я думаю, что это очень маловероятно.
Вы всегда можете использовать longs и упаковать данные в себя, чтобы повысить эффективность. Тогда вы всегда можете гарантировать, что будете использовать все 4 байта.
Читая вышеупомянутые комментарии, кажется, что мой вывод станет неожиданностью для многих (это также удивит меня), поэтому стоит повторить:
- Старый размер (int) == size(byte) для переменных больше не сохраняется, по крайней мере в Sun Java 6.
Вместо этого размер (байт) == 1 байт (!!)
Просто хотел отметить, что заявление
Вы можете хранить не более +127 в байте Java
не совсем правильно.
Вы всегда можете хранить 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
Смотрите мой MonitoringTools на моем сайте (www.csd.uoc.gr/~andreou)
класс X { байт b1, b2, b3...; } long memoryUsed = MemoryMeasurer.measure (new X ());
(Может использоваться и для более сложных объектов / графов объектов)
В Sun 1.6 JDK кажется, что байт действительно занимает один байт (в более старых версиях, интбайт с точки зрения памяти). Но обратите внимание, что даже в более старых версиях byte[] также был упакован в один байт на запись.
В любом случае, дело в том, что нет необходимости в сложных тестах, подобных описанным выше Джоном Скитом, которые дают только оценки. Мы можем напрямую измерить размер объекта!
Похоже, что ответ, скорее всего, будет зависеть от вашей версии JVM, а также от архитектуры вашего процессора. Линейка процессоров Intel эффективно обрабатывает байты (благодаря 8-битной истории процессоров). Некоторые чипы RISC требуют выравнивания слов (4 байта) для многих операций. А выделение памяти может быть различным для переменных в стеке, полей в классе и в массиве.