Ответы:
Mindprod отмечает, что это не простой вопрос:
JVM может хранить данные любым удобным для себя способом, с прямым или прямым порядком байтов, с любым количеством дополнений или накладных расходов, хотя примитивы должны вести себя так, как если бы они имели официальные размеры.
Например, JVM или собственный компилятор может решить хранитьboolean[]
в 64-разрядных длинных чанках, таких как aBitSet
. Вам не нужно об этом говорить, пока программа дает одинаковые ответы.
- Это может выделить некоторые временные объекты в стеке.
- Он может полностью оптимизировать некоторые переменные или вызовы методов, заменяя их константами.
- Он может создавать версии методов или циклов, т. Е. Компилировать две версии метода, каждая из которых оптимизирована для определенной ситуации, а затем заранее решить, какой из них вызвать.
Тогда, конечно, аппаратное обеспечение и ОС имеют многоуровневые кэши: кэш-кэш-память, кэш-память SRAM, кэш-память DRAM, обычный набор оперативной памяти и резервное хранилище на диске. Ваши данные могут дублироваться на каждом уровне кэша. Вся эта сложность означает, что вы можете только очень приблизительно предсказать потребление ОЗУ.
Вы можете использовать Instrumentation.getObjectSize()
для получения оценки хранилища, потребляемого объектом.
Чтобы визуализировать фактическое расположение объекта, площадь и ссылки, вы можете использовать инструмент JOL (Java Object Layout) .
В современном 64-битном JDK объект имеет 12-байтовый заголовок, дополненный кратным 8 байтам, поэтому минимальный размер объекта составляет 16 байт. Для 32-разрядных JVM служебные данные составляют 8 байтов, дополненные кратным 4 байта. (Из ответа Дмитрия Спихальского , ответа Джейена и JavaWorld .)
Как правило, ссылки являются 4 байтами на 32-битных платформах или на 64-битных платформах до -Xmx32G
; и 8 байтов выше 32 Гб ( -Xmx32G
). (См. Ссылки на сжатые объекты .)
В результате для 64-разрядной JVM обычно требуется на 30-50% больше пространства кучи. ( Должен ли я использовать 32- или 64-разрядную JVM? 2012, JDK 1.7)
Оболочки в штучной упаковке имеют накладные расходы по сравнению с примитивными типами (из JavaWorld ):
Integer
: 16-байтовый результат немного хуже, чем я ожидал, посколькуint
значение может уместиться всего в 4 дополнительных байта. Использование ресурсовInteger
обходится мне на 300 процентов из-за нехватки памяти по сравнению с тем, когда я могу сохранить значение как примитивный тип
Long
: 16 байт также: очевидно, что фактический размер объекта в куче зависит от низкоуровневого выравнивания памяти, выполняемого конкретной реализацией JVM для конкретного типа процессора. Похоже, что этоLong
8 байтов служебных данных объекта плюс еще 8 байтов для фактического длинного значения. Напротив,Integer
имелось неиспользуемое 4-байтовое отверстие, скорее всего потому, что JVM I использует принудительное выравнивание объекта на границе 8-байтового слова.
Другие контейнеры тоже дороги:
Многомерные массивы : это еще один сюрприз.
Разработчики обычно используют конструкции, какint[dim1][dim2]
в численных и научных вычислениях.В
int[dim1][dim2]
экземпляре массива каждый вложенныйint[dim2]
массив являетсяObject
самостоятельным. Каждый добавляет обычные 16-байтовые массивы. Когда мне не нужен треугольный или рваный массив, это означает чистые накладные расходы. Воздействие возрастает, когда размеры массива сильно различаются.Например,
int[128][2]
экземпляр занимает 3600 байт. По сравнению с 1040 байтами, используемымиint[256]
экземпляром (который имеет такую же емкость), 3600 байтов представляют 246-процентную служебную нагрузку. В крайнем случаеbyte[256][1]
коэффициент накладных расходов составляет почти 19! Сравните это с ситуацией C / C ++, в которой тот же синтаксис не добавляет никаких издержек при хранении.
String
:String
рост памяти a отслеживает рост его внутреннего массива символов. Тем не менее,String
класс добавляет еще 24 байта служебной информации.Для непустых
String
символов размером 10 или менее добавленные накладные расходы относительно полезной полезной нагрузки (2 байта для каждого символа плюс 4 байта для длины) варьируются от 100 до 400 процентов.
Рассмотрим этот пример объекта :
class X { // 8 bytes for reference to the class definition
int a; // 4 bytes
byte b; // 1 byte
Integer c = new Integer(); // 4 bytes for a reference
}
Наивная сумма предполагает, что экземпляр X
будет использовать 17 байтов. Однако из-за выравнивания (также называемого заполнением) JVM выделяет память кратно 8 байтам, поэтому вместо 17 байт будет выделено 24 байта.
Это зависит от архитектуры / JDK. В современной архитектуре JDK и 64-битной архитектуре объект имеет 12-байтовый заголовок и заполнение на 8 байт, поэтому минимальный размер объекта составляет 16 байт. Вы можете использовать инструмент под названием Java Object Layout, чтобы определить размер и получить подробную информацию о макете объекта и внутренней структуре любого объекта или угадать эту информацию по ссылке на класс. Пример вывода для Integer в моей среде:
Running 64-bit HotSpot VM.
Using compressed oop with 3-bit shift.
Using compressed klass with 3-bit shift.
Objects are 8 bytes aligned.
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
java.lang.Integer object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 12 (object header) N/A
12 4 int Integer.value N/A
Instance size: 16 bytes (estimated, the sample instance is not available)
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
Так, для Integer размер экземпляра составляет 16 байтов, потому что 4 байта int сжимаются на месте сразу после заголовка и перед границей заполнения.
Пример кода:
import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.util.VMSupport;
public static void main(String[] args) {
System.out.println(VMSupport.vmDetails());
System.out.println(ClassLayout.parseClass(Integer.class).toPrintable());
}
Если вы используете Maven, чтобы получить JOL:
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.3.2</version>
</dependency>
Каждый объект имеет определенные накладные расходы для связанной с ним информации о мониторе и типе, а также самих полей. Кроме того, поля могут быть размечены в значительной степени, однако JVM сочтет нужным (я полагаю) - но, как показано в другом ответе , по крайней мере, некоторые JVM будут упакованы достаточно плотно. Рассмотрим такой класс:
public class SingleByte
{
private byte b;
}
против
public class OneHundredBytes
{
private byte b00, b01, ..., b99;
}
На 32-битной JVM я бы ожидал, что 100 экземпляров SingleByte
примут 1200 байтов (8 байтов служебной информации + 4 байта для поля из-за заполнения / выравнивания). Я ожидаю один случайOneHundredBytes
займет 108 байтов - служебные данные, а затем 100 байтов, упакованных. Тем не менее, она может варьироваться в зависимости от JVM - одна реализация может принять решение не упаковывать поля, в результате OneHundredBytes
чего она занимает 408 байт (= 8 байт + 4 * 100 выровненных / дополненных байт). На 64-битной JVM издержки тоже могут быть больше (не уверен).
РЕДАКТИРОВАТЬ: см. Комментарий ниже; по-видимому, HotSpot дополняет границы до 8 байт вместо 32, поэтому каждый экземплярSingleByte
занимает 16 байтов.
В любом случае, «один большой объект» будет, по крайней мере, столь же эффективным, как несколько небольших объектов - для простых случаев, подобных этому.
Общая используемая / свободная память программы может быть получена в программе через
java.lang.Runtime.getRuntime();
Время выполнения имеет несколько методов, которые связаны с памятью. Следующий пример кодирования демонстрирует его использование.
package test;
import java.util.ArrayList;
import java.util.List;
public class PerformanceTest {
private static final long MEGABYTE = 1024L * 1024L;
public static long bytesToMegabytes(long bytes) {
return bytes / MEGABYTE;
}
public static void main(String[] args) {
// I assume you will know how to create a object Person yourself...
List < Person > list = new ArrayList < Person > ();
for (int i = 0; i <= 100000; i++) {
list.add(new Person("Jim", "Knopf"));
}
// Get the Java runtime
Runtime runtime = Runtime.getRuntime();
// Run the garbage collector
runtime.gc();
// Calculate the used memory
long memory = runtime.totalMemory() - runtime.freeMemory();
System.out.println("Used memory is bytes: " + memory);
System.out.println("Used memory is megabytes: " + bytesToMegabytes(memory));
}
}
Похоже, что каждый объект имеет служебную информацию 16 байтов в 32-битных системах (и 24 байта в 64-битных системах).
http://algs4.cs.princeton.edu/14analysis/ является хорошим источником информации. Одним из примеров многих хороших является следующий.
http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf также очень информативен, например:
Является ли пространство памяти, потребляемое одним объектом с 100 атрибутами, таким же, как у 100 объектов с одним атрибутом каждый?
Нет.
Сколько памяти выделено для объекта?
Сколько дополнительного пространства используется при добавлении атрибута?
Вопрос будет очень широким.
Это зависит от переменной класса, или вы можете называть использование памяти состояний в Java.
Он также имеет некоторые дополнительные требования к памяти для заголовков и ссылок.
Куча памяти, используемая объектом Java, включает
память для примитивных полей, согласно их размеру (см. ниже Размеры примитивных типов);
память для справочных полей (4 байта каждое);
заголовок объекта, состоящий из нескольких байтов «служебной» информации;
Для объектов в java также требуется некоторая служебная информация, такая как запись класса объекта, идентификатора и флагов состояния, например, доступен ли объект в данный момент, заблокирована ли синхронизация и т. Д.
Размер заголовка объекта Java варьируется в 32 и 64-битных jvm.
Хотя это основные потребители памяти, jvm также требует дополнительных полей, например, для выравнивания кода и т. Д.
Размеры примитивных типов
логическое значение & байт - 1
char & short - 2
int & float - 4
длинный и двойной - 8
Я получил очень хорошие результаты от подхода java.lang.instrument.Instrumentation, упомянутого в другом ответе. Хорошие примеры его использования см. В статье « Счетчик памяти инструментария из информационного бюллетеня JavaSpecialists» и в библиотеке java.sizeOf на SourceForge.
В случае, если это кому-нибудь пригодится, вы можете загрузить с моего веб-сайта небольшой Java-агент для запроса использования памяти объектом . Это также позволит вам запрашивать «глубокое» использование памяти.
(String, Integer)
кешем Guava для каждого элемента. Спасибо!
Правила о том, сколько памяти используется, зависят от реализации JVM и архитектуры ЦП (например, 32-битная или 64-битная).
Для подробных правил для SUN JVM проверьте мой старый блог
С уважением, Маркус