Анализ использования памяти: Java против C ++

9

Как использование памяти целочисленного объекта, написанного на Java, сравнивается / контрастирует с использованием памяти целочисленного объекта, написанного на C ++? Разница незначительна? Нет разницы? Большая разница? Я предполагаю, что это то же самое, потому что int является int независимо от языка (?)

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

Что меня поразило, так это количество памяти, необходимое для создания одного объекта Java. Взять, к примеру, целочисленный объект. Поправьте меня, если я ошибаюсь, но целочисленный объект Java требует 24 байта памяти:

  • 4 байта для его переменной экземпляра int
  • 16 байтов служебной информации (ссылка на класс объекта, информация о сборке мусора и информация о синхронизации)
  • 4 байта заполнения

В качестве другого примера, массив Java (который реализован как объект) требует 48 + байтов:

  • 24 байта информации заголовка
  • 16 байтов служебных данных объекта
  • 4 байта для длины
  • 4 байта для заполнения
  • плюс память, необходимая для хранения значений

Как эти использования памяти сравниваются с тем же кодом, написанным на C ++?

Раньше я не замечал использования памяти написанными мною программами на С ++ и Java, но теперь, когда я начинаю изучать алгоритмы, я больше ценю ресурсы компьютера.

Энтони
источник
6
Что такое "целочисленный объект" в C ++? int? Если это так, вы должны сравнить это с intJava, а не Integer- до тех пор, пока ваши целые числа C ++ 32-битные.
Мат
+1 Если я создал класс c ++, который имел только одну переменную int, то создал его экземпляр
Anthony
3
int не является int - он зависит от платформы
1
C ++, имеющий только один член типа int, обычно не будет иметь никаких накладных расходов. Он будет использовать столько же места, сколько платформа использует для хранения int (обычно 4 байта на современных платформах ПК).
Дирк Холсоппл
+1 для программиста на Java, интересующегося памятью. Более всего, осведомленность о памяти является наиболее важным фактором, определяющим производительность на современных архитектурах.
Ималлетт

Ответы:

15

Это зависит от платформы и реализации.

C ++ гарантирует, что размер charсоставляет ровно один байт и ширину не менее 8 бит. Тогда размер a short intне менее 16 бит и не меньше, чем char. Размер intпо крайней мере такой же большой, как размер short int. Размер long intне менее 32 бит и не меньше, чем int.

sizeof(char) == 1; sizeof(long int) >= sizeof(int) >= sizeof(short int) >= sizeof(bool) >= sizeof(char).

Фактическая модель памяти C ++ очень компактна и предсказуема . Например, в объектах, массивах или указателях нет метаданных. Структуры и классы являются смежными, как и массивы, но отступы могут быть размещены там, где это необходимо и необходимо.

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

zxcdw
источник
1
Правда, но для сравнения я бы сказал, что мы должны предполагать целочисленные типы эквивалентного размера (даже если они доступны только под разными именами). В конце концов, разные размеры означают разную семантику, и на многих (не на всех) общих платформах размеры идентичны, или целые числа одного размера доступны под разными именами.
Примечание для OP: Вам лучше выбрать размер целого числа - если вы хотите использовать 32-битный тип int в C ++, вы можете использовать его int32_t.
Стефф
9

Большинство ответов, кажется, игнорируют пару довольно важных моментов.

Во-первых, в очень большом количестве Java вы практически никогда не видите необработанного int- почти все его использование имеет смысл, Integerпоэтому тот факт, что intразмер может быть (примерно) такого же размера, что и intв C или C ++, почти не имеет значения, за исключением этого ( по моему опыту, небольшой) процент кода, который использует только intвместо Integer.

Во-вторых, размеры отдельных объектов не имеют ничего общего с объемом памяти программы в целом. В Java использование памяти программой связано, прежде всего, с тем, как был настроен сборщик мусора. В большинстве случаев GC настроен на максимизацию скорости, что (в значительной степени) означает запуск GC как можно реже.

В данный момент у меня нет удобной ссылки, но были некоторые тесты, показывающие, что Java может работать на той же скорости, что и C, но для этого вам нужно запускать GC достаточно редко, чтобы он использовал примерно в 7 раз больше Память. Это не потому, что отдельные объекты в 7 раз больше, а потому что GC может стать довольно дорогим, если вы делаете это слишком часто. Хуже того, GC может освободить память только тогда, когда он может «доказать», что больше нет никакого способа получить доступ к объекту, а не просто, когда вы знаете, что с ним покончено. Это означает, что даже если вы запускаете GC намного чаще, чтобы минимизировать использование памяти, вы, вероятно, все еще можете планировать, что типичная программа имеет больший объем памяти. В таком случае вы можете уменьшить коэффициент до 2 или 3 вместо 7. Однако, даже если вы сильно переборщите, не стоит1 .

В зависимости от ситуации, есть и другой фактор, который может или не может быть значительным: память, занятая самой JVM. Это более или менее исправлено, так что в процентном отношении оно может быть огромным, если приложению не нужно много собственного хранилища, или может быть незначительным, если приложению нужно хранить много. По крайней мере, на моей машине даже самое тривиальное Java-приложение занимает примерно 20-25 мегабайт (может быть более 1000x для тривиальных программ или почти неизмеримо крошечным для больших).


1 Это не значит, что никому не удавалось написать Java с таким размером, который был бы близок к тому, что вы получили бы в C ++. Это просто говорит о том, что наличие одного и того же количества / размера объектов и очень частый запуск GC не приведут вас к этому, как правило.

Джерри Гроб
источник
7
Что касается вашего первого замечания: я не Java-парень, но API-интерфейсы Java, которые я видел, никогда не использовали Integer(почему бы им?) Вместо int. Только универсальные коллекции не имеют другого выбора, кроме как использовать Integerиз-за стирания типов, но если вы заботитесь, вы можете заменить их на реализацию, специализированную для intили любого другого примитивного типа, который вам нужен. И затем есть временный бокс для прохождения через универсальный код переноса (например, все, что требует Object[]). Кроме того, есть ли у вас источники для пространства GC? Я в этом не сомневаюсь, мне просто любопытно.
3
@delnan: cs.canisius.edu/~hertzm/gcmalloc-oopsla-2005.pdf
Джерри Коффин
9

Я надеюсь, вы понимаете, что все это глубоко зависит от реализации, как для Java, так и для C ++. При этом объектная модель Java требует довольно много места.

Объекты C ++ нет (вообще) нужно любое хранение , за исключением того, что нужно члены. Обратите внимание, что (в отличие от Java, где все определяемое пользователем является ссылочным типом), клиентский код может использовать объекты как тип значения или как ссылочные типы, то есть объект может хранить указатель / ссылку на другой объект или напрямую сохранять объект без косвенного Один дополнительный указатель на объект необходим, если есть какие-либо virtualметоды, но довольно много полезных классов предназначены для обхода без полиморфизма и не нуждаются в этом. Нет метаданных GC и нет блокировки для каждого объекта. Таким образом, class IntWrapper { int x; public: IntWrapper(int); ... };объектам нужно не больше места, чем обычным ints, и они могут быть размещены непосредственно (т.е. без косвенного обращения) в коллекциях и других объектах.

Массивы хитры просто потому, что в C ++ нет готовых, общих эквивалентов Java Array. Вы можете просто выделить кучу объектов new[](без каких-либо дополнительных данных / метаданных), но поле длины отсутствует - реализация, вероятно, хранит один, но вы не можете получить к нему доступ. std::vectorявляется динамическим массивом и, следовательно, имеет дополнительные накладные расходы и больший интерфейс. std::arrayи массивы в стиле C (int arr[N];), нужна постоянная времени компиляции. Теоретически, это должно быть только хранилище объекта плюс одно целое число для длины - но, поскольку вы можете получить динамическое изменение размера и полнофункциональный интерфейс с очень небольшим дополнительным пространством, вы просто делаете это на практике. Обратите внимание, что все эти, а также все остальные коллекции по умолчанию хранят объекты по значению, таким образом сохраняя косвенность и пространство для ссылок, а также улучшая поведение кэша. Вы должны явно хранить указатели (умные, пожалуйста), чтобы получить косвенное указание.

Вышеприведенные сравнения не совсем справедливы, поскольку некоторые из этих сбережений обеспечиваются отсутствием функций, включаемых в Java, и их эквивалент C ++ часто менее оптимизирован, чем эквивалент Java (*). Общий способ реализации virtualв C ++ накладывает столько же накладных расходов, сколько и общий способ реализации virtualв Java. Чтобы получить блокировку, вам нужен полностью функциональный объект мьютекса, который, скорее всего, больше нескольких бит. Чтобы получить подсчет ссылок ( неэквивалентно GC и не должен использоваться как таковой, но иногда полезен), вам нужен умный указатель, который добавляет поле счетчика ссылок. Если объект не построен тщательно, счетчик ссылок, объект интеллектуального указателя и объект, на который ссылаются, находятся в совершенно разных местах, и даже если вы правильно его построите, общий указатель может (должен?) По-прежнему быть двумя указателями вместо одного. С другой стороны, хороший стиль C ++ не использует эти функции настолько, чтобы это имело значение - на практике объекты хорошо написанной библиотеки C ++ используют меньше. Это не обязательно означает меньшее использование памяти в целом, но это означает, что C ++ имеет хороший старт в этом отношении.

(*) Например, вы можете получать виртуальные вызовы, идентификационные хеш-коды и блокировку только одним словом для некоторых объектов (и двумя словами для многих других объектов), объединяя информацию о типе с различными флагами и удаляя биты блокировки для объектов, которые вряд ли понадобятся замки. См. Эффективность пространства и времени реализации объектной модели Java (PDF) Дэвида Ф. Бэкона, Стивена Дж. Финка и Дэвида Гроува для подробного объяснения этой и других оптимизаций.


источник
3

Обычный intв Java занимает столько же места, сколько intв C ++, при условии, что обе реализации используют одинаковый целочисленный размер и выравнивание памяти.

Объект int ( целое в штучной упаковке , то есть экземпляр класса Integer) несет все издержки экземпляра класса в Java, поэтому он значительно больше, чем intв C ++. Однако, если бы вы оснастили объект в C ++ теми же возможностями, что и Java-объекты, входящие в комплект поставки (полиморфизм, бокс, сборка мусора, RTTI), то вы, вероятно, в конечном итоге получили бы объект, равный размер.

И тогда есть соображения оптимизации; Поскольку модели выполнения и парадигмы программирования различаются, маловероятно, что какая-либо нетривиальная проблема будет решена одинаково на обоих языках, поэтому сравнение размера хранилища на этом уровне не имеет невероятного смысла.

Да, объекты Java по умолчанию несут больше накладных расходов, чем классы C ++, но они имеют больше возможностей, и это приводит к другому стилю программирования - хороший программист может использовать преимущества как положительного, так и отрицательного сторон любого языка.

tdammers
источник
+1 Больше накладных расходов, но больше возможностей в Java, теперь я понимаю, спасибо
Энтони