Что занимает меньше памяти строка или массив

Обновлено: 04.07.2024

Скажем, у меня есть строка, которая читает "Мой текст", в какой форме эта строка будет использовать больше памяти в виде байта или строки?

спросил(а) 2009-05-27T01:15:00+04:00 12 лет, 5 месяцев назад ответил(а) 2009-05-27T01:20:00+04:00 12 лет, 5 месяцев назад

Это зависит от кодировки символа массива байтов. Вы можете преобразовать любую строку в массив байтов, но вам нужно выбрать кодировку; нет единого стандартного или правильного кодирования. То, что раньше называлось ASCII, бесполезно вне англоязычного мира.

В большинстве кодировок "Мой текст" будет иметь длину 7 байтов. Но бросьте некоторые символы с акцентом в Европе или японские символы, а те (если они могут быть представлены вообще) могут иметь более одного или двух байтов каждый. В некоторых кодировках с некоторыми текстовыми строками представление байтового массива может быть больше внутреннего представления Unicode, используемого System.String .

ответил(а) 2009-05-27T01:28:00+04:00 12 лет, 5 месяцев назад

Быть Unicode не означает, что строка будет содержать более одного байта на символ, это просто означает, что он "может" принимать более одного байта на символ.

ответил(а) 2009-05-27T01:28:00+04:00 12 лет, 5 месяцев назад

Размер байтового массива будет зависеть от кодировки, которую вы используете для представления текста. Если вы используете UTF-16, например

вы, очевидно, получаете одинаковые 14 байтов. Если вы используете UTF-8:

вы получите массив из 7 байтов:

Это тот же размер (и такое же представление) как ASCII, потому что ваш пример использует только символы, доступные в кодировке ASCII. Все эти символы, по определению, одинаковы в UTF-8.

Теперь, если вы используете не-ASCII-символы, скажем, японский "日", для кодировки UTF-8 потребуется 3 байта:

UTF-16 потребуется всего 2 байта:

Попытка конвертировать японский символ в ASCII даст исключение или просто использует "?" в зависимости от того, как вы настраиваете Encoding , потому что ASCII не может представлять ничего, кроме символов ASCII.

Другой немного другой пример, европейский характер "ä". 2 байта в UTF-8:

В UTF-16 также 2 байта:

ASCII не может представлять этот символ.

Подводя итог, потребляемая память зависит от фактических данных в ваших строках и от того, какую кодировку вы используете для ее представления.

Все вышеперечисленное говорит о потреблении памяти для необработанных данных, обратите внимание, что для вычисления общего потребления памяти вам также необходимо включить metadata​​strong > , который является частью каждого массива и строки, например, ее длина, а в случае .net-строк - также нулевой ограничитель (2 дополнительные байты со значением "0" ). Количество байтов для метаданных является постоянным и относительно небольшим, поэтому любая разница между строкой и массивом будет иметь значение только в том случае, если у вас есть тонны очень маленьких текстов.

В массив записывается больше, чем было выделено памяти
Я выделил 10 байт для хранения массива типа char, но я могу в него записать почему-то больше.

Убить процесс, если тот потребляет количество оперативной памяти больше установленного значения
Здравствуйте. Собственно, весь вопрос в заголовке. Как это реализовать? Заранее спасибо.


Дан массив вещественных чисел. Найти кол-во чисел больше, чем сумма дробных частей
double mas = < 20.2500, 1.069, 1.06 >; double c = 0; int d = 0; .

novagc, по памяти - да, ибо массив - объект и к размеру хранимых данных прибавляется аж целых 12\24 байта данных самого объекта. По скорости - нет.

Решение

novagc,
Немного уточню ответ.
Во-первых, нужно разделять два случая - массив структурного типа (int, float, double, bool и т.д.) и массив ссылочного типа (string, object и т.д.).

Далее, для структурного типа: Массив чуть больше потребляет памяти, поскольку является объектом и содержит доп. информацию в памяти (те самые 12 байт для x86 и 24 байта для x64).

Для ссылочного типа объектов массив занимает значительно больше памяти, поскольку он хранит адреса (ссылки) на объекты. Например, массив из 100 объектов типа string хранит 100 адресов (а это 400 байт для x86, 800 байт для x64). Плюс к этому - в памяти хранятся сами объекты string. Таким образом, массив из 100 объектов типа string занимает на 400 байт больше чем просто 100 переменных типа string.

Далее, по скорости доступа. Уважаемый Usaga, вероятно имел ввиду что скорость доступа к массиву и скорость доступа к переменной равны. Это так. Но в вопросе наверно вы имели ввиду скорость доступа к элементу массива.

Так вот скорость доступа к элементу массива конечно меньше чем скорость доступа к переменной.
Для массива нужно сначала взять адрес самого массива, прибавить к нему значение индекса, получить адрес объекта и затем уже прочитать сам объект. В случае переменной же - достаточно просто прочитать значение (адрес переменной известен заранее).

Таким образом, в целом, отвечая на ваш вопрос - массив занимает больше памяти (особенно в случае ссылочных типов) и доступ к элементам массива медленнее, чем доступ к переменным.

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 байт. Хотя это условие и не обязательно.

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

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

Скажем, у меня есть строка, которая читает «Мой текст», в какой форме эта строка будет занимать больше памяти, в виде байта или строки?

Это зависит от кодировки символов байтового массива. Вы можете преобразовать любую строку в массив байтов, но вы должны выбрать кодировку; не существует единого стандарта или правильной кодировки. То, что раньше называлось ASCII, бесполезно за пределами англоязычного мира.

В большинстве кодировок «Мой текст» будет иметь длину 7 байтов. Но добавьте некоторые символы с европейским акцентом или японские символы, и они (если они вообще могут быть представлены) могут быть больше одного или двух байтов каждый. В некоторых кодировках с некоторыми текстовыми строками представление байтового массива может быть больше, чем внутреннее представление Unicode, используемое System.String .

Использование Unicode не означает, что строка будет занимать более одного байта на символ, это просто означает, что она «может» занимать более одного байта на символ.

Размер байтового массива будет зависеть от кодировки , которую вы используете для представления текста. Если вы используете UTF-16 , вот так

Вы, очевидно, получите те же 14 байтов. Если вместо этого вы используете UTF-8 :

Вы получите массив из 7 байтов:

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

Теперь, если вместо этого вы используете символы, отличные от ASCII , скажем японский «日», для кодировки UTF-8 потребуется 3 байта:

UTF-16 потребует всего 2 байта:

Попытка преобразовать японский символ в ASCII приведет к исключению или просто используйте знак "?" в зависимости от того, как вы настроили Encoding , потому что ASCII не может представлять ничего, кроме символов ASCII.

Другой немного другой пример, европейский иероглиф «ä». 2 байта в UTF-8:

Также 2 байта в UTF-16:

ASCII не может представлять этот символ.

Подводя итог, потребляемая память зависит от фактических данных в ваших строках и от того, какую кодировку вы используете для их представления .

Все вышесказанное говорит о потреблении памяти только для необработанных данных , обратите внимание, что для расчета общего потребления памяти вам также необходимо включить метаданные , который является частью каждого массива и строки, например, его длина , а в случае строк .net также нулевой терминатор (2 дополнительных байта с значение "0"). Количество байтов для метаданных является постоянным и относительно небольшим, поэтому любая разница между строкой и массивом будет иметь значение только в том случае, если у вас есть тонны очень маленьких текстов.

Массив байтов будет занимать меньше памяти, если у вас нет нескольких копий строки, и в этом случае строка будет занимать меньше памяти благодаря таблице строк.

Но реальный вопрос: действительно ли это важно? Использование строки в виде строки дает множество преимуществ, а не ее хранение в виде массива байтов.

Подробностей не знаю, так как ваш вопрос был очень узким, но я чувствую преждевременную оптимизацию.

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