Сколько памяти занимает string java

Обновлено: 07.07.2024

В Java, если у меня есть строка x , как я могу вычислить количество байтов в этой строке?

Столбец базы данных может иметь ограничение длины в байтах, например VARCHAR2 (4000 BYTE) в Oracle. Возможно, вы захотите узнать количество байтов строки в желаемой кодировке, чтобы узнать, подходит ли строка. Я полагаю, что есть два возможных толкования этого вопроса, в зависимости от цели: во-первых, «сколько памяти использует моя строка?». Ответ на этот вопрос предоставлен @roozbeh ниже (возможно, по модулю тонкостей ВМ, таких как сжатые OOPS). Другой - «если я преобразую строку в byte [], сколько памяти будет использовать этот байтовый массив?». На этот вопрос отвечает Анджей Дойл. Разница может быть большой: «Hello World» в UTF8 составляет 11 байтов, но String (на @roozbeh) составляет 50 байтов (если моя математика верна). Я должен был добавить, что 11 байтов не включают в себя служебные данные объекта byte [], который их содержит, поэтому сравнение несколько вводит в заблуждение.

Строка - это список символов (т.е. кодовые точки). Количество байтов, взятых для представления строки, полностью зависит от того, какую кодировку вы используете, чтобы превратить ее в байты .

Тем не менее, вы можете превратить строку в байтовый массив, а затем посмотреть на его размер следующим образом:

Итак, вы видите, что даже простая строка "ASCII" может иметь различное количество байтов в своем представлении, в зависимости от используемой кодировки. В качестве аргумента используйте любой набор символов, который вас интересует getBytes() . И не попадайтесь в ловушку, предполагая, что UTF-8 представляет каждый символ как один байт, поскольку это также не так:

(Обратите внимание, что если вы не предоставите аргумент набора символов, будет использоваться набор символов платформы по умолчанию . Это может быть полезно в некоторых контекстах, но в целом следует избегать зависимости от значений по умолчанию и всегда использовать явный набор символов при кодировании / требуется расшифровка.)

Я читал много о распределении памяти для строк в последнее время и не могу найти никаких деталей, если все то же самое с Java 8.

Сколько пространства памяти будет использовать String как "Alexandru Tanasescu" в Java 8? Я использую 64-битную версию.

ОТВЕТЫ

Ответ 1

Использование памяти минимальной строки:

Общие сведения об использовании памяти строк ( ИСТОЧНИК)

  • a char массив - таким образом отдельный объект, содержащий фактические символы;
  • целочисленное смещение в массив, с которого начинается строка;
  • длина строки;
  • другой int для кэшированного вычисления хэш-кода.

Если String содержит, скажем, 19 символов, то сам объект String по-прежнему требует 24 байта. Но теперь для массива char требуется 12 байт заголовка плюс 19 * 2 = 38 байт для семнадцати символов. Так как 12 + 38 = 50 не кратно 8, нам также нужно округлить до следующего кратного 8 (56). Таким образом, наш 19-символьный String будет использовать 56 + 24 = 80 байт.

В Java 8 больше нет offset и length . Только hash и CharArray .
@Thomas Jungblut

  • a char массив - таким образом отдельный объект, содержащий фактические символы;
  • целочисленное смещение в массив, с которого начинается строка;
  • длина строки;
  • другой int для кэшированного вычисления хэш-кода.

Итак, в Java8 способ вычисления памяти для строк остается таким же, но вы должны вычесть 8 байт меньше из-за отсутствующих offset и length .

Ответ 2

Если вы посмотрите на источники Oracle Java 8, у вас есть:

A char value[] и a int hash . A char - 2 байта, а int - 4 байта.

Таким образом, ответ не будет yourstring.length * 2 + 4?

Нет. У каждого предмета были накладные расходы. Например, массив хранит свои размеры. И как массив (объект), так и строка будут нести дополнительную память от сборщика мусора, хранящего информацию о них.

Нет надежного способа вычислить это, потому что AFAIK каждый JRE и JDK не несет ответственности за размер служебных данных объекта.

Ответ 3

"Alexandru Tanasescu" использует 104 байта. Вот как получить размер

Примечание: запустите его с помощью опции -XX: -UseTLAB

Ответ 4

Текущая реализация класса String хранит символы в char, используя два байта (шестнадцать бит) для каждого символа.

В Java SE 9 это может измениться.

Обратите внимание, что, поскольку это JEP не JSR (и он упоминает реализацию), я понимаю, что это реализация, специфичная и не определенная JLS.

Integer vs int

Все мы знаем, что в java — everything is an object. Кроме, пожалуй, примитивов и ссылок на сами объекты. Давайте рассмотрим две типичных ситуации:

В этих простых строках разница просто огромна, как для JVM так и для ООП. В первом случае, все что у нас есть — это 4-х байтная переменная, которая содержит значение из стека. Во втором случае у нас есть ссылочная переменная и сам объект, на который эта переменная ссылается. Следовательно, если в первом случае мы определено знаем, что занимаемый размер равен:

Забегая вперед скажу — во втором случае количество потребляемой памяти приблизительно в 5 раз больше и зависит от JVM. А теперь давайте разберемся, почему разница настолько огромна.

Из чего же состоит объект?
  • Заголовок объекта;
  • Память для примитивных типов;
  • Память для ссылочных типов;
  • Смещение/выравнивание — по сути, это несколько неиспользуемых байт, что размещаются после данных самого объекта. Это сделано для того, чтобы адрес в памяти всегда был кратным машинному слову, для ускорения чтения из памяти + уменьшения количества бит для указателя на объект + предположительно для уменьшения фрагментации памяти. Стоит также отметить, что в java размер любого объекта кратен 8 байтам!
Структура заголовка объекта
  • Маркировочное слово (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. Теперь исходя из информации выше получаем:

Теперь заглянем в класс строки:

И подсчитаем размер:

Ну и это еще не все… Так как строка содержит ссылку на массив символов, то, по сути, мы имеем дело с двумя разными объектами — объектом класса String и самим массивом, который хранит строку. Это, как бы, верно с точки зрения ООП, но если посмотреть на это со стороны памяти, то к полученному размеру нужно добавить и размер выделенного для символов массива. А это еще 12 байт на сам объект массива + 2 байта на каждый символ строки. Ну и, конечно же, не забываем добавлять выравнивание для кратности 8 байтам. Итого в конечном итоге простая, казалось бы, строка new String(«a») выливается в:

Важно отметить, что new String(«a») и new String(«aa») будут занимать одинаковое количество памяти. Это важно понимать. Типичный пример использования этого факта в свою пользу — поле hash в классе String. Если бы его не было, то объект строки так или иначе занимал бы 24 байта, за счет выравнивания. А так получается что для этих 4-х байтов нашлось очень достойное применение. Гениальное решение, не правда ли?

Размер ссылки

Немножко хотел бы оговорится о ссылочных переменных. В принципе, размер ссылки в JVM зависит от ее разрядности, подозреваю, что для оптимизации. Поэтому в 32-х разрядных JVM размер ссылки обычно 4 байта, а в 64-х разрядных — 8 байт. Хотя это условие и не обязательно.

Группировка полей
Зачем все это?

Иногда возникает ситуация в которой Вам необходимо прикинуть приблизительный объем памяти для хранения тех или иных объектов, например словаря, эта маленькая справка поможет быстро сориентироваться. Также, это потенциально возможный способ оптимизации, особенно в том окружении, где доступ к его настройкам не доступен.

В последнее время я много читал о распределении памяти для строк и не могу найти никаких деталей, если то же самое происходит с Java 8.

Сколько памяти будет использовать строка типа "Alexandru Tanasescu" в Java 8? Я использую 64-битную версию.

Java7 или ниже

Минимальное использование памяти String:

Понимание использования памяти строк ( SOURCE )

  • массив символов, то есть отдельный объект, содержащий действительные символы;
  • целочисленное смещение в массив, с которого начинается строка;
  • длина строки;
  • другое int для кэшированного вычисления хеш-кода.

Если String содержит, скажем, 19 символов, то самому объекту String все еще требуется 24 байта. Но теперь массив символов требует 12 байтов заголовка плюс 19 * 2 = 38 байтов для семнадцати символов. Поскольку 12 + 38 = 50 не кратно 8, нам также нужно округлить до следующего кратного 8 (56). В общем, наша 19-символьная переменная String будет использовать 56 + 24 = 80 байт.

Java8.

Java 8 больше не имеет offset и length . Только hash и CharArray .
@ Томас Юнгблут

  • массив символов, то есть отдельный объект, содержащий действительные символы;
  • целочисленное смещение в массив, с которого начинается строка;
  • длина строки;
  • другое int для кэшированного вычисления хеш-кода.

Таким образом, в Java8 способ вычисления памяти для строк остается тем же, но вы должны вычесть на 8 байт меньше из-за отсутствия offset и length .

Если вы посмотрите на источники Oracle Java 8, у вас есть:

char value[] и int hash . char составляет 2 байта, а int составляет 4 байта.

Так не будет ли ответ yourstring.length * 2 + 4?

У каждого объекта были накладные расходы. Массив хранит свои размеры, например. И массив (объект) и строка будут занимать дополнительную память от сборщика мусора, хранящего информацию о них.

Нет надежного способа рассчитать это, потому что AFAIK каждый JRE и JDK не имеет обязательств по размеру служебных данных объекта.

Эта статья покажет вам, сколько памяти занимает написанный вами объект Java?

Модель памяти заголовка объекта Java

32

Давайте сначала посмотрим, какова модель памяти объекта Java? Поскольку наши виртуальные машины делятся на 32-битные и 64-битные, в их моделях должны быть различия.Ниже я перечисляю 32-битные виртуальные машины и 64-битные виртуальные машины. Java Модель памяти заголовка объекта. Чтобы

64

64

Поскольку местная среда автора 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 Есть несколько ситуаций распространения:

  1. Разблокирован (нормальный):Хэш-код ( identity_hashcode ) Занимает 31 бит, возраст поколения ( age ) Занимает 4 бита, режим отклонения ( biased_lock ) Занимает 1 бит, метка блокировки ( lock ) Занимает 2 бита, а остальные 26 бит не используются (то есть все 0)
  2. Предвзято:Идентификатор потока занимает 54 бита, epoch Занимает 2 бита, возраст поколения ( age ) Занимает 4 бита, режим смещения (biased_lock) занимает 1 бит, флаг блокировки (lock) занимает 2 бита, а оставшийся 1 бит не используется.
  3. Легкий закрытый: Указатель блокировки занимает 62 бита, метка блокировки ( lock ) Занимает 2 бита.
  4. Тяжелый вес заблокирован (Тяжелый вес заблокирован): Указатель блокировки занимает 62 бита, метка блокировки ( lock ) Занимает 2 бита.
  5. Отметка 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 байта. Так зачем вам выравнивание памяти? Есть две основные причины:

  • Причина платформы (переносимости): не все аппаратные платформы могут получить доступ к любым данным по любому адресу. Например: конкретная аппаратная платформа позволяет получать только определенные типы данных по определенному адресу, иначе это вызовет ненормальную ситуацию.
  • Причины производительности: если вы обращаетесь к невыровненной памяти, это приведет к тому, что ЦП выполнит два доступа к памяти, и потребуются дополнительные тактовые циклы для обработки выравнивания и вычислений. Выравниваемой памяти требуется только один доступ для завершения действия чтения.

Я использую легенду, чтобы проиллюстрировать процесс доступа ЦП к выравниванию без памяти:


  1. ЦП впервые считывает первый блок памяти с невыровненным адресом, считывая байты 0–3. И удалите ненужный байт 0.
  2. ЦП снова считывает второй блок памяти по невыровненному адресу, считывая 4-7 байтов. И удалите ненужные байты 5, 6, 7 байт.
  3. Объедините 1-4 байта данных.
  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. Основные моменты заключаются в следующем:

  1. Модель памяти заголовка объекта Java отличается в 32-разрядных виртуальных машинах и 64-разрядных виртуальных машинах. 64-разрядная виртуальная машина разделена на две модели заголовков объектов с включенным сжатием указателя и отключенным сжатием указателя, поэтому существует три типа моделей заголовков объектов. .
  2. Выравнивание памяти происходит в основном по причинам платформы и производительности. В этой статье в основном анализируются причины производительности.
  3. При вычислении заполнения памяти пустым объектом необходимо вычислить выравнивание памяти, а при вычислении памяти для непустого объекта необходимо добавить ссылочную память и исходный объект-экземпляр.

Прилагается Java / C / C ++ / Машинное обучение / Алгоритм и структура данных / Внешний интерфейс / Android / Python / Программист должен прочитать / Список книг:

Читайте также: