Размер Java объектов / Хабр
Знаете сколько в памяти занимает строка? Каких только я не слышал ответов на этот вопрос, начиная от «не знаю» до «2 байта * количество символов в строке». А сколько тогда занимает пустая строка? А знаете сколько занимает объект класса Integer? А сколько будет занимать Ваш собственный объект класса с тремя Integer полями? Забавно, но ни один мой знакомый Java программист не смог ответить на эти вопросы… Да, большинству из нас это вообще не нужно и никто в реальных java проектах не будет об этом думать. Но это, ведь, как не знать объем двигателя машины на которой Вы ездите. Вы можете быть прекрасным водителем и даже не подозревать о том, что значат цифры 2.4 или 1.6 на вашей машине. Но я уверен, что найдется мало людей, которые не знакомы со значением этих цифр. Так почему же java программисты так мало знают об этой части своего инструмента?
Integer vs int
Все мы знаем, что в java — everything is an object. Кроме, пожалуй, примитивов и ссылок на сами объекты. Давайте рассмотрим две типичных ситуации:
//первый случай int a = 300; //второй случай Integer b = 301;
В этих простых строках разница просто огромна, как для JVM так и для ООП. В первом случае, все что у нас есть — это 4-х байтная переменная, которая содержит значение из стека. Во втором случае у нас есть ссылочная переменная и сам объект, на который эта переменная ссылается. Следовательно, если в первом случае мы определено знаем, что занимаемый размер равен:
sizeOf(int)
то во втором:
sizeOf(reference) + sizeOf(Integer)
Забегая вперед скажу — во втором случае количество потребляемой памяти приблизительно в 5 раз больше и зависит от JVM. А теперь давайте разберемся, почему разница настолько огромна.
Из чего же состоит объект?
Прежде чем определять объем потребляемой памяти, следует разобраться, что же JVM хранит для каждого объекта:
- Заголовок объекта;
- Память для примитивных типов;
- Память для ссылочных типов;
- Смещение/выравнивание — по сути, это несколько неиспользуемых байт, что размещаются после данных самого объекта. Это сделано для того, чтобы адрес в памяти всегда был кратным машинному слову, для ускорения чтения из памяти + уменьшения количества бит для указателя на объект + предположительно для уменьшения фрагментации памяти. Стоит также отметить, что в java размер любого объекта кратен 8 байтам!
Структура заголовка объекта
Каждый экземпляр класса содержит заголовок. Каждый заголовок для большинства JVM(Hotspot, openJVM) состоит из двух машинных слов. Если речь идет о 32-х разрядной системе, то размер заголовка — 8 байт, если речь о 64-х разрядной системе, то соответственно — 16 байт. Каждый заголовок может содержать следующую информацию:
- Маркировочное слово (mark word) — к сожалению мне так и не удалось найти назначение этой информации, подозреваю что это просто зарезервированная на будущее часть заголовка.
- Hash Code — каждый объект имеет хеш код. По умолчанию результат вызова метода Object.hashCode() вернет адрес объекта в памяти, тем не менее некоторые сборщики мусора могут перемещать объекты в памяти, но хеш код всегда остается одним и тем же, так как место в заголовке объекта как раз может быть использовано для хранения оригинального значения хеш кода.
- Garbage Collection Information — каждый java объект содержит информацию нужную для системы управления памятью. Зачастую это один или два бита-флага, но также это может быть, например, некая комбинация битов для хранения количества ссылок на объект.
- Type Information Block Pointer — содержит информацию о типе объекта. Этот блок включает информацию о таблице виртуальных методов, указатель на объект, который представляет тип и указатели на некоторые дополнительные структуры, для более эффективных вызовов интерфейсов и динамической проверки типов.
- Lock — каждый объект содержит информацию о состоянии блокировки. Это может быть указатель на объект блокировки или прямое представление блокировки.
- Array Length — если объект — массив, то заголовок расширяется 4 байтами для хранения длины массива.
Спецификация Java
Известно, что примитивные типы в Java имеют предопределенный размер, этого требует спецификация для переносимости кода.
Поэтому не будем останавливаться на примитивах, так как все прекрасно описано по ссылке выше. А что же говорит спецификация для объектов? Ничего, кроме того, что у каждого объекта есть заголовок. Иными словами, размеры экземпляров Ваших классов могут отличатся от одной JVM к другой. Собственно, для простоты изложения я буду приводить примеры на 32-х разрядной Oracle HotSpot JVM. А теперь давайте разберем самые используемые классы Integer и String.Integer и String
Итак, давайте попробуем подсчитать сколько же будет занимать объект класса Integer в нашей 32-х разрядной HotSpot JVM. Для этого нужно будет заглянуть в сам класс, нам интересны все поля, которые не объявлены как static. Из таких видим только одно — int value. Теперь исходя из информации выше получаем:
Заголовок: 8 байт Поле int: 4 байта Выравнивание для кратности 8 : 4 байта Итого: 16 байт
private final char value[]; private final int offset; private final int count; private int hash;
И подсчитаем размер:
Заголовок: 8 байт Поля int: 4 байта * 3 == 12 байт Ссылочная переменная на объект массива: 4 байта Итого: 24 байта
Ну и это еще не все… Так как строка содержит ссылку на массив символов, то, по сути, мы имеем дело с двумя разными объектами — объектом класса String и самим массивом, который хранит строку. Это, как бы, верно с точки зрения ООП, но если посмотреть на это со стороны памяти, то к полученному размеру нужно добавить и размер выделенного для символов массива. А это еще 12 байт на сам объект массива + 2 байта на каждый символ строки. Ну и, конечно же, не забываем добавлять выравнивание для кратности 8 байтам. Итого в конечном итоге простая, казалось бы, строка new String(«a») выливается в:
new String() Заголовок: 8 байт Поля int: 4 байта * 3 == 12 байт Ссылочная переменная на объект массива: 4 байта Итого: 24 байта new char[1] Заголовок: 8 байт + 4 байта на длину массива == 12 байт Примитивы char: 2 байта * 1 == 2 байта Выравнивание для кратности 8 : 2 байта Итого: 16 байта Итого, new String("a") == 40 байт
Важно отметить, что new String(«a») и new String(«aa») будут занимать одинаковое количество памяти. Это важно понимать. Типичный пример использования этого факта в свою пользу — поле hash в классе String. Если бы его не было, то объект строки так или иначе занимал бы 24 байта, за счет выравнивания. А так получается что для этих 4-х байтов нашлось очень достойное применение. Гениальное решение, не правда ли?
Размер ссылки
Немножко хотел бы оговорится о ссылочных переменных. В принципе, размер ссылки в JVM зависит от ее разрядности, подозреваю, что для оптимизации. Поэтому в 32-х разрядных JVM размер ссылки обычно 4 байта, а в 64-х разрядных — 8 байт. Хотя это условие и не обязательно.
Группировка полей
Следует также отметить, что JVM проводит предварительную группировку полей объекта. Это значит, что все поля класса размещаются в памяти в определенном порядке, а не так как объявлены. Порядок группировки выглядит так:
- 1. 8-ми байтовые типы(double и long)
- 2. 4-х байтовые типы(int и float)
- 3. 2-х байтовые типы(short и char)
- 4. Одно байтовые типы(
booleanи byte) - 5. Ссылочные переменные
Зачем все это?
Иногда возникает ситуация в которой Вам необходимо прикинуть приблизительный объем памяти для хранения тех или иных объектов, например словаря, эта маленькая справка поможет быстро сориентироваться.
Также, это потенциально возможный способ оптимизации, особенно в том окружении, где доступ к его настройкам не доступен.Выводы
Тема памяти в java очень интересна и обширна, когда я начинал писать эту статью, то думал что уложусь в пару примеров с выводами. Но чем дальше и глубже копаешь, тем больше и интересней становится. Вообще, знать как выделяется память для объектов очень полезная вещь, так как поможет Вам сэкономить память, предотвратить подобные проблемы или оптимизировать вашу программу в местах, где это казалось невозможным. Конечно, места где можно использовать такие оптимизации — очень редки, но все же… Надеюсь статья была Вам интересной.
Рекомендуемый стиль кода — Платформа CUBA. Руководство по разработке приложений
4.1. Рекомендуемый стиль кода
Форматирование кода
Для Java и Groovy кода рекомендуется придерживаться стандартного стиля, описанного в документе Code Conventions for the Java Programming Language. При программировании в IntelliJ IDEA для этого достаточно использовать стиль по умолчанию, а для переформатирования применять сочетание клавиш Ctrl-Alt-L.
Максимальная длина строки − 120 символов. Длина отступа — 4 символа, использование пробелов вместо символов табуляции включено.
XML код: длина отступа — 4 символа, использование пробелов вместо символов табуляции включено.
Соглашения по именованию
Идентификатор | Правило именования | Пример |
---|---|---|
Java и Groovy классы | ||
Класс контроллера экрана | UpperCamelCase Контроллер экрана списка сущностей − Контроллер экрана редактирования − |
|
XML дескрипторы экранов | ||
Идентификатор компонента, имена параметров в запросах | lowerCamelCase, только буквы и цифры |
|
Идентификатор источника данных | lowerCamelCase, только буквы и цифры, оканчивается на Ds |
|
SQL скрипты | ||
Зарезервированные слова | lowercase |
|
Таблицы | UPPER_CASE. Название предваряется именем проекта для формирования пространства имен. В именах таблиц рекомендуется использовать единственное число. |
|
Колонки | UPPER_CASE |
|
Колонки внешних ключей | UPPER_CASE. Состоит из имени таблицы, на которую ссылается колонка (без префикса проекта), и суффикса _ID. |
|
Индексы | UPPER_CASE. Состоит из префикса IDX_, имени таблицы, для которой создается индекс (с префиксом проекта), и имен полей, включенных в индекс. |
|
android. Каков максимальный объем данных, который может содержать строка в java?
Я знаю, что это может быть действительно нубский вопрос, но я не нашел хорошего надежного источника, где я мог бы получить эту информацию. 31 — 1 (или приблизительно 2 миллиарда)
Теоретически у вас может быть String из 2 147 483 647 символов. Я не думаю, что вам нужно намного больше, чем это.
Однако, как указал @TedHopp в комментариях и опубликовал в своем ответе, система Android ограничивает пространство кучи до 16 МБ. Таким образом, вы никогда практически не сможете достичь теоретического предела, а максимальная длина строки будет где-то в диапазоне от 4 до 64 миллионов символов.
5
Это, вероятно, будет ограничено размером кучи, доступным приложению. Это может зависеть как от модели телефона, так и от оператора/производителя телефона. Для устройств Android в ответе здесь перечислены максимальные размеры кучи для различных моделей телефонов; они варьируются от 16 до 256 МБ.
Обратите внимание, что Java хранит строки в виде кодов UTF-16, поэтому каждый символ занимает два байта. Независимо от того, добавляете ли вы строки напрямую или используете StringBuilder (намного лучше), вам иногда потребуется вдвое больше памяти: один для хранения существующей строки и один для хранения новой строки/буфера, когда его необходимо расширить.
Если рассматривать все вместе, максимальная длина строки, вероятно, будет порядка от 4 до 64 миллионов символов, в зависимости от телефона (намного меньше теоретического максимума 2 31 -1).
Ящерица Билл ответила на этот вопрос
Вы должны быть в состоянии получить строку длины Integer.MAX_VALUE (всегда 2147483647 (2 31 — 1) по спецификации Java, максимальный размер массива, который класс String использует для внутреннего хранения) или половину максимального размера размер кучи (поскольку каждый символ занимает два байта), в зависимости от того, что меньше.
Однако для объединения последовательностей строк следует использовать StringBuilder, созданный для таких целей.
1
404: Страница не найдена
Страница, которую вы пытались открыть по этому адресу, похоже, не существует. Обычно это результат плохой или устаревшей ссылки. Мы извиняемся за любые неудобства.
Что я могу сделать сейчас?
Если вы впервые посещаете TechTarget, добро пожаловать! Извините за обстоятельства, при которых мы встречаемся. Вот куда вы можете пойти отсюда:
Поиск- Узнайте последние новости.
- Наша домашняя страница содержит самую свежую информацию о Java-разработке.
- Наша страница «О нас» содержит дополнительную информацию о сайте, на котором вы находитесь, TheServerSide.com.
- Если вам нужно, свяжитесь с нами, мы будем рады услышать от вас.
Просмотр по категории
Архитектура приложения
- Необработанный, но растущий потенциал банковского обслуживания без ядра
Несмотря на то, что банковское дело без ядра все еще является новой концепцией, оно демонстрирует большой потенциал для освобождения банков от жестких программных систем, которые…
- Основы достижения высокой сплоченности и низкой связанности
Легко сказать «высокая сплоченность, низкая связанность», но так ли легко это реализовать на практике? Мы рассмотрим некоторые основы. ..
- Как обнаружить и контролировать распространение теневых API
Теперь, когда проникновение через API стало излюбленным методом хакеров, ИТ-специалистам необходимо предпринять дополнительные шаги для защиты этих…
Качество ПО
- Тестовые фреймворки и примеры для модульного тестирования кода Python
Модульное тестирование является важным аспектом разработки программного обеспечения. Команды могут использовать Python для модульного тестирования, чтобы оптимизировать преимущества Python…
- Атрибуты эффективной стратегии тестирования базы данных
Команды должны внедрить правильную стратегию тестирования базы данных для оптимизации результатов. Изучите эффективные атрибуты тестирования базы данных…
- Обновления Java 20 Project Loom готовят почву для Java LTS
Java 20 повторно инкубирует две функции масштабируемости Project Loom, что делает их главными кандидатами на то, чтобы стать стандартом в сентябрьском выпуске Java 9. 0003
Облачные вычисления
- Подходит ли вам облачная стратегия?
Стратегия, ориентированная на облачные технологии, имеет свои преимущества и недостатки. Узнайте, как избежать рисков и построить стратегию, которая …
- Как использовать сценарии запуска в Google Cloud
Google Cloud позволяет использовать сценарии запуска при загрузке виртуальных машин для повышения безопасности и надежности. Выполните следующие действия, чтобы создать свой…
- Когда использовать AWS Compute Optimizer и Cost Explorer
AWS Compute Optimizer и Cost Explorer отслеживают, анализируют и оптимизируют затраты на облако. Сравните два инструмента, чтобы выбрать, какой из них …
Безопасность
- Microsoft запускает Security Copilot на базе искусственного интеллекта
Microsoft Security Copilot — это помощник на основе искусственного интеллекта для специалистов по информационной безопасности, который сочетает в себе технологию OpenAI GPT-4 с программным. ..
- Публично раскрытые атаки программ-вымогателей в США в 2023 году
База данных программ-вымогателей TechTarget Editorial собирает общедоступные раскрытия информации, уведомления и подтвержденные отчеты об атаках на …
- Сравните моделирование взлома и атаки с тестированием на проникновение
Подробное изучение нарушений и имитации атак по сравнению с тестированием на проникновение показывает, что оба инструмента предотвращают нарушения периметра и данных. Найти …
ПоискAWS
- AWS Control Tower стремится упростить управление несколькими учетными записями
Многие организации изо всех сил пытаются управлять своей огромной коллекцией учетных записей AWS, но Control Tower может помочь. Услуга автоматизирует…
- Разбираем модель ценообразования Amazon EKS
В модели ценообразования Amazon EKS есть несколько важных переменных.