Java сколько памяти занимает пустой объект без полей
Я пытался найти эффективные типы данных. Я знаю, что int - это 4 байта, а char-один байт.
- объект, содержащий пять целых чисел (4 * 5 = 20 байт)
- строковый объект, состоящий из десяти символов. (Предположим, что он имеет 10 символов 10 * 1 = 10 байт)
Я прав? Как вы думаете, какой из них лучше?
Возможные Дубликаты : Программно вычислить память, занимаемую объектом Java, включая объекты, на которые он ссылается В Java, каков наилучший способ определить размер объекта? Привет В java .. нет оператора sizeof Но как я могу узнать размер объекта в java? Спасибо
Сначала объективный ответ:
- Примитивные типы данных описаны здесь
- Строки сложнее, потому что JVM может их интернировать. Смотрите хорошее объяснение здесь
Не очень объективный ответ: выберите структуру данных, которая обеспечивает наилучший дизайн для вашего приложения.
Если у вас есть конкретное ограничение в приложении, опубликуйте более подробную информацию о данных, которые вам необходимо обработать, и о существующих ограничениях.
String -это не просто массив символов, это независимый объект, и у него есть поля, отличные от его резервного char[] . Например, String имеет три поля int : offset , count и hash . Таким образом, пустая строка обычно составляет 16 байт (поскольку нам также необходимо учитывать поле char[] ) плюс обычные 8 байт накладных расходов объекта. Также обратите внимание, что char[] сам по себе является объектом и имеет поле int length и связанные с ним накладные расходы объекта. Как только вы все это учтете, вы можете добавить два (а не один!) байта на символ.
Итак, для 10-символьной строки:
Это составляет около 60 байт. Я говорю "about", потому что кое-что из этого зависит от VM.
Вы ошибаетесь насчет char s в Java: поскольку они предназначены для хранения 16-битных кодовых точек UNICODE, они занимают два, а не один байт каждый . В конце концов, оба представления будут занимать одинаковый объем памяти.
Вы должны выбрать тип данных, который имеет наибольший смысл для вас, разработчика ваших классов и читателей вашего кода. Проблемы с памятью не должны быть на первом месте в ваших приоритетах проектирования, если только количество необходимых объектов не угрожает переполнением доступной памяти. Даже в этом случае вам следует тщательно профилировать память перед оптимизацией.
Символы имеют размер 2 байта. Они эквивалентны короткому знаку без знака, поэтому значение символа может варьироваться от [0, 65535] включительно.
Количество байтов, занимаемых строкой, на самом деле равно:
Таким образом, в вашем примере строка из 10 символов занимает 20 байт, а не 10 байт.
Это будет просто содержимое строки . В классе String есть и другие переменные, которые, конечно, будут занимать больше байтов. И даже пустой объект занимает определенное количество байтов, которое будет варьироваться в зависимости от реализации JVM.
Однако только содержимое символа будет занимать 2 байта на символ.
Но не беспокойтесь об этом, поскольку это, безусловно, преждевременная оптимизация. Чистый код обычно важнее, чем молниеносный код . Выберите подходящие типы данных, напишите код, который легко следовать и читать. Эти вещи более важны.
Если вы беспокоитесь о хранении больших строк в памяти, подумайте о том, чтобы изменить свой подход. Самая распространенная проблема, которую я вижу с большими строками, - это когда новые программисты читают весь файл в память.
Если вы делаете это, попробуйте обрабатывать данные построчно. Держите в памяти только самую маленькую единицу, которая вам нужна, выполняйте обработку и двигайтесь дальше.
Возможный Дубликат : В Java, каков наилучший способ определить размер объекта? В Actionscript я обычно могу просто сделать: var myVar:uint = 5; getSize(myVar) //outputs 4 bytes Как мне это сделать в Java?
Каков максимальный размер объекта hashmap/map в c++ и java? Я хочу использовать hashmap, но я работаю с огромными данными. Я беспокоюсь, что если я использую его на больших данных, он может выйти из строя из-за ограничения его емкости. Так ли это? Если да, то какой может быть альтернативный путь?
Эта статья покажет вам, сколько памяти занимает написанный вами объект Java?
Модель памяти заголовка объекта Java
Давайте сначала посмотрим, какова модель памяти объекта Java? Поскольку наши виртуальные машины делятся на 32-битные и 64-битные, в их моделях должны быть различия.Ниже я перечисляю 32-битные виртуальные машины и 64-битные виртуальные машины. Java Модель памяти заголовка объекта. Чтобы
Поскольку местная среда автора jdk1.8 , 64-битная виртуальная машина, здесь я использую 64-битную виртуальную машину (сжатие открытых указателей) для анализа, потому что по умолчанию jdk1.8 Сжатие указателя включено по умолчанию в 64-битных виртуальных машинах.
Заголовок объекта Java в основном состоит из двух частей, первая часть - Mark Word , Это тоже Java Важная часть принципа реализации блокировки, а другая часть Klass Word 。
Klass WordНа самом деле это дизайн виртуальной машины oop-klass model Модель здесь OOP Относится к Ordinary Object Pointer (Обычный указатель на объект), похоже, что указатель на самом деле является объектом, скрытым в указателе. А также klass Содержит метаданные и информацию о методах для описания Java Класс. Он занимает 32-битное пространство в среде, где сжатый указатель включен в 64-битной виртуальной машине.
Mark WordЭто является предметом нашего анализа, и здесь также будут представлены соответствующие знания о замках. Mark Word Занимает 64-битное пространство в 64-битной среде виртуальной машины. весь Mark Word Есть несколько ситуаций распространения:
- Разблокирован (нормальный):Хэш-код ( identity_hashcode ) Занимает 31 бит, возраст поколения ( age ) Занимает 4 бита, режим отклонения ( biased_lock ) Занимает 1 бит, метка блокировки ( lock ) Занимает 2 бита, а остальные 26 бит не используются (то есть все 0)
- Предвзято:Идентификатор потока занимает 54 бита, epoch Занимает 2 бита, возраст поколения ( age ) Занимает 4 бита, режим смещения (biased_lock) занимает 1 бит, флаг блокировки (lock) занимает 2 бита, а оставшийся 1 бит не используется.
- Легкий закрытый: Указатель блокировки занимает 62 бита, метка блокировки ( lock ) Занимает 2 бита.
- Тяжелый вес заблокирован (Тяжелый вес заблокирован): Указатель блокировки занимает 62 бита, метка блокировки ( lock ) Занимает 2 бита.
- Отметка GC: Бит метки занимает 2 бита, остальные пустые (то есть заполнены 0)
Выше приведен наш анализ модели памяти заголовка объекта Java.Пока это объект Java, он обязательно будет включать заголовок объекта, что означает, что эту часть использования памяти невозможно избежать.Следовательно, в среде авторской 64-битной виртуальной машины и Jdk1.8 (сжатие указателя включено) ни один объект ничего не делает. Пока класс объявлен, его использование памяти составляет не менее 96 бит, то есть не менее 12 байт.
Модель валидации
Давайте напишем код для проверки указанной выше модели памяти. Здесь рекомендуется Openjdk.веселый инструмент, Это может помочь вам просмотреть использование памяти объектом.
Сначала добавьте зависимость от maven
Давайте сначала посмотрим: если мы просто создадим новый обычный класс без добавления каких-либо атрибутов, сколько места это займет?
Согласно нашему предыдущему анализу модели объектной памяти Java, пустой объект имеет только один заголовок объекта, который будет занимать 96 бит, то есть 12 байтов, при условии сжатия указателя.
Запустите инструмент, чтобы просмотреть занятое пространство
Вышеупомянутая строка кода проанализирует, сколько памяти вы создаете новый объект NullObject, который занят. Давайте выполним это и посмотрим, что из этого получится:
Здесь мы находим результаты шоу: Instance size:16 bytes , Результат - 16 байт. 12 байт, которые мы предсказали ранее, отличаются. Почему это происходит? Мы видим, что на приведенном выше рисунке есть 3 строки заголовка объекта, каждая из которых занимает 4 байта, поэтому размер заголовка составляет 12 байтов, что согласуется с нашими расчетами.Последняя строка - это 4 байта, заполненные виртуальной машиной.Так почему виртуальная машина должна заполнять 4 байта?
Выравнивание памяти
Хотите знать, почему виртуальная машина должна заполнять 4 байта, нам нужно понять, что такое выравнивание памяти?
Мы, программисты, смотрим на память так:
На рисунке выше показан метод чтения из памяти одной косточки и одной моркови. Но на самом деле ЦП не считывает и не записывает память по одному байту. Напротив, ЦП считывает память блок за блоком, а размер блока может составлять 2, 4, 6, 8, 16 байтов и т. Д. Мы называем размер блока гранулярностью доступа к памяти. Как показано ниже:
Предполагая, что процессор 32-битной платформы, он будет читать блок памяти с детализацией 4 байта. Так зачем вам выравнивание памяти? Есть две основные причины:
- Причина платформы (переносимости): не все аппаратные платформы могут получить доступ к любым данным по любому адресу. Например: конкретная аппаратная платформа позволяет получать только определенные типы данных по определенному адресу, иначе это вызовет ненормальную ситуацию.
- Причины производительности: если вы обращаетесь к невыровненной памяти, это приведет к тому, что ЦП выполнит два доступа к памяти, и потребуются дополнительные тактовые циклы для обработки выравнивания и вычислений. Выравниваемой памяти требуется только один доступ для завершения действия чтения.
Я использую легенду, чтобы проиллюстрировать процесс доступа ЦП к выравниванию без памяти:
- ЦП впервые считывает первый блок памяти с невыровненным адресом, считывая байты 0–3. И удалите ненужный байт 0.
- ЦП снова считывает второй блок памяти по невыровненному адресу, считывая 4-7 байтов. И удалите ненужные байты 5, 6, 7 байт.
- Объедините 1-4 байта данных.
- Поместите в реестр после слияния.
Следовательно, невыполнение выравнивания памяти приведет к тому, что ЦП выполнит дополнительные операции чтения и потребует дополнительных вычислений. Если выравнивание памяти выполнено, ЦП может напрямую начать чтение с адреса 0, и желаемые данные могут быть прочитаны один раз без дополнительных операций чтения и вычислений, что экономит время работы.Мы использовали пространство для времени, поэтому нам нужно выравнивание памяти.
Возвращаясь к пустому объекту Java, заполненному 4 байтами,Поскольку исходный заголовок байта составляет 12 байтов, на 64-битных машинах, если память выровнена, это 128 бит, что составляет 16 байтов, поэтому нам нужно заполнить 4 байта.
Расчет непустых объектов занимает память
Мы знаем, что пустой объект занимает 16 байтов, поэтому сколько байтов занимает непустой объект? Мы по-прежнему пишем общий класс для проверки:
Этот демонстрационный класс знакомит с другими объектами, которые мы знаем int Тип - 4 байта, NullObject Объект занимает 16 байт, заголовок объекта - 12 байт,Есть еще одна очень важная ситуация NullObject В текущем классе это ссылка, поэтому реальный объект не будет сохранен, а будет храниться только адрес ссылкиАдрес ссылки занимает 4 байта, поэтому всего 12 + 4 + 4 = 20 байтов, а память выровнена по 24 байтам. Проверим, является ли этот результат:
Результаты приведены ниже:
Мы это видим TestNotNull Пространство, занимаемое классом, составляет 24 байта, из которых заголовок занимает 12 байтов, а переменная a да int Тип, занимают 4 байта, переменная nullObject Это ссылка, которая занимает 4 байта и, наконец, заполняет 4 байта, что в сумме составляет 24 байта, что согласуется с нашим предыдущим прогнозом.Но поскольку мы создали NullObject , Этот объект будет существовать в памяти какое-то время, поэтому нам также нужно добавить 16 байтов памяти для этого объекта, что в сумме составляет 24 байта + 16 байтов = 40 байтов. Последний результат статистической печати на нашем изображении также составляет 40 байт, так что наш анализ верен.
Это также идея того, как анализировать, сколько памяти на самом деле занимает объект. Согласно этой идее, плюс инструмент jol из openJDK, вы можете в основном определить, сколько памяти теряет ваш «объект».
подводить итоги
В этой статье я в основном описываю, как анализировать, сколько места в памяти занимает объект Java. Основные моменты заключаются в следующем:
- Модель памяти заголовка объекта Java отличается в 32-разрядных виртуальных машинах и 64-разрядных виртуальных машинах. 64-разрядная виртуальная машина разделена на две модели заголовков объектов с включенным сжатием указателя и отключенным сжатием указателя, поэтому существует три типа моделей заголовков объектов. .
- Выравнивание памяти происходит в основном по причинам платформы и производительности. В этой статье в основном анализируются причины производительности.
- При вычислении заполнения памяти пустым объектом необходимо вычислить выравнивание памяти, а при вычислении памяти для непустого объекта необходимо добавить ссылочную память и исходный объект-экземпляр.
Прилагается Java / C / C ++ / Машинное обучение / Алгоритм и структура данных / Внешний интерфейс / Android / Python / Программист должен прочитать / Список книг:
Является ли пространство памяти, потребляемое одним объектом с 100 атрибутами, таким же, как у 100 объектов с одним атрибутом каждый?
Сколько памяти выделено для объекта?
Сколько дополнительного пространства используется при добавлении атрибута?
Mindprod отмечает, что это не простой вопрос:
- Это может выделить некоторые временные объекты в стеке.
- Он может полностью оптимизировать некоторые переменные или вызовы методов, заменяя их константами.
- Он может создавать версии методов или циклов, т. Е. Компилировать две версии метода, каждая из которых оптимизирована для определенной ситуации, а затем заранее решить, какой из них вызвать.
Методы измерения
Вы можете использовать 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 ):
Другие контейнеры тоже дороги:
Многомерные массивы : это еще один сюрприз.
Разработчики обычно используют конструкции, как 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 процентов.
центровка
Наивная сумма предполагает, что экземпляр X будет использовать 17 байтов. Однако из-за выравнивания (также называемого заполнением) JVM выделяет память кратно 8 байтам, поэтому вместо 17 байт будет выделено 24 байта.
Мне нужно узнать, сколько байт в оперативной памяти занимает определенная коллекция в моем приложении ( ArrayList ). Нужно это для того, чтобы представлять, сколько объектов я могу в нее добавить, не получив OutOfMemory .
Как посчитать вручную я представляю, но хотел бы узнать, есть ли способ посчитать программно.
Конкретно свою проблему я решил, но вопрос остается открытым, ибо мое решение не связано с определением объема памяти, занимаемого объекта.
Итак, я просто решил на практике проверить, сколько же объектов без проблем удержится в памяти на разных моделях телефонов.
java.lang.instrument в Android (Dalvic/ART) не существует.
-
Самый простой способ — сделать Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory() до создания достаточно большой тестовой коллекции и после. Повторить несколько раз, чтобы исключить случайности.
Более сложный — это использовать java.lang.instrumentation package и т.п. для определения размера среднего объекта в коллекции и по нему прикидывать размер коллекции.
Сам по себе ArrayList занимает всего 4(8) байт на элемент (в зависимости от размера указателя, причем даже в 64 битной системе указатели могут быть 4 байтные), то есть основные затраты памяти будут именно на объекты, содержащиеся в ArrayList . Но определение размера объекта в Java вещь сложная, так как не понятно что считать. Если считать только размер самих полей-ссылок — размер объектов будет совсем небольшим, если считать в том числе и все объекты, на которые данные объекты ссылаются, то много объектов могут ссылаться на один общий объект и он посчитается несколько раз (более того все объекты в системе могут ссылаться друг на друга и размер любого объекта можно посчитать равным всем объектам системы). Так что первый способ предпочтительнее.
Читайте также: