Аналіз використання пам’яті: Java проти C ++ незначно?


9

Як використання пам'яті цілого об'єкта, написаного на Java, порівнює \ контраст із використанням пам'яті цілого об'єкта, записаним на C ++? Чи різниця незначна? Без різниці? Велика різниця? Я здогадуюсь, що це те саме, що int - це int незалежно від мови (?)

Причина, чому я запитав це, полягає в тому, що я читав про важливість знати, коли вимоги до пам'яті програми заважатимуть програмісту вирішити задану проблему.

Що мене зачарувало - це об'єм пам'яті, необхідний для створення одного об’єкта Java. Візьмемо для прикладу цілий об'єкт. Виправте мене, якщо я помиляюся, але для цілого об’єкта Java потрібно 24 байти пам'яті:

  • 4 байти для його змінної інстанції
  • 16 байт накладних витрат (посилання на клас об'єкта, інформація про збирання сміття та інформацію про синхронізацію)
  • 4 байти прокладки

В якості іншого прикладу, для масиву Java (який реалізується як об'єкт) потрібно 48 + байт:

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

Як ці звичаї в пам'яті порівнюються з тим самим кодом, написаним на C ++?

Раніше я не забував про використання пам'яті програм C ++ та Java, про які писав, але тепер, коли я починаю дізнаватися про алгоритми, я більше вдячний за ресурси комп’ютера.


6
Що таке "цілий об'єкт" в C ++? int? Якщо так, то вам слід порівняти це з intJava, а не Integer- поки ваші входи C ++ становлять 32 біт.
Мат

+1 Якщо я створив клас c ++, який мав лише одну змінну int, то застосував його
Ентоні

3
int не є int - він залежить від платформи

1
C ++ із лише одним int членом зазвичай не матиме накладних витрат. Він використовуватиме рівно стільки місця, скільки платформа використовує для зберігання інтерту (як правило, 4 байти на поточних платформах ПК).
Дірк Холсоппл

+1 для Java-програміста цікавить пам'ять. Більше всього іншого, обізнаність із пам’яттю є найважливішим фактором, що визначає ефективність сучасних архітектур.
imallett

Відповіді:


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, ніж від коду, який вона працює.


1
Щоправда, але для порівняння я б сказав, що ми повинні вважати цілі типи однакового розміру (навіть якщо вони доступні лише під різними іменами). Адже різні розміри означають різну семантику, а на багатьох (не всіх) загальних платформах розміри однакові, або цілі числа одного розміру доступні під різними назвами.

Примітка до ОП: Вам може бути краще вибрати розмір цілого числа - якщо ви хочете 32-розрядний int в C ++, ви можете використовувати int32_t.
K.Steff

9

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

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

По-друге, розміри окремих об'єктів майже не мають нічого спільного з слідом пам'яті програми в цілому. На Яві слід пам'яті програми стосується насамперед того, як налаштовано сміттєзбірник. У більшості випадків GC налаштовується на максимальну швидкість, що (в значній мірі) означає запускати GC якомога рідше.

На даний момент у мене немає зручного посилання, але були деякі тести, які показують, що Java може працювати з тією ж швидкістю, що і С, але для цього вам доведеться запускати GC досить рідко, що він використовує приблизно в 7 разів більше пам'ять. Це не тому, що окремі об’єкти в 7 разів більше, а тому, що GC може бути досить дорогим, якщо ви робите це занадто часто. Гірше, що GC може вивільнити пам'ять лише тоді, коли вона може "довести", що більше немає жодного способу отримати доступ до об'єкта, а не просто тоді, коли ви знаєте, що все зробили з його використанням. Це означає, що навіть якщо ви запускаєте GC набагато частіше, щоб мінімізувати використання пам’яті, ви, мабуть, все-таки плануєте на типовій програмі мати більший слід пам’яті. У такому випадку ви можете зменшити коефіцієнт до 2 або 3 замість 7. Навіть якщо ви різко переходите за борт, однак не робіть1 .

Залежно від ситуації, існує ще один фактор, який може бути або не бути значним: пам'ять, яку займає сам JVM. Це більш-менш фіксовано, тому у відсотках він може бути величезним, якщо додатку не потрібно багато власного сховища, або може бути незначним, якщо додатку потрібно зберігати багато. Принаймні, на моїй машині, здається, навіть найтривітніше додаток Java займає щось на зразок 20-25 мегабайт (це може бути понад 1000 разів для програм тривлалу, або майже незмірно крихітних для великих).


1 Це не означає, що нікому не вдалося написати Java з відбитком, наближеним до того, що ви отримаєте з C ++. Варто лише сказати, що просто наявність однакової кількості / розміру об'єктів та запуск GC дійсно часто не приведе вас туди, як правило.


7
Щодо вашого першого моменту: я не хлопець на Java, але Java API, який я бачив, ніколи не використовував Integer(навіщо це?) Замість цього int. Тільки загальні колекції не мають іншого вибору, ніж використовувати Integerчерез стирання типу, але якщо ви піклуєтесь, ви можете замінити ці програми на спеціалізовану програму intчи будь-який примітивний тип, який вам потрібен. А потім є тимчасовий бокс для проходження загального коду загортання (наприклад, все, що вимагає Object[]). Окрім цього, чи є у вас джерела для накладних просторів GC? Я не дуже сумніваюся в цьому, мені просто цікаво.


9

Я сподіваюся, ви зрозуміли, що все це є глибоко визначеним впровадженням, як для Java, так і для C ++. При цьому, об'єктна модель Java вимагає небагато місця.

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

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

Наведені вище порівняння не є абсолютно справедливими, оскільки деякі з цих заощаджень забезпечуються, не включаючи функції, що включають Java, а їх C ++ еквівалент часто менш оптимізований, ніж еквівалент Java (*). Загальний спосіб реалізації virtualв C ++ накладає рівно стільки ж накладних витрат, скільки загальний спосіб впровадження virtualв Java. Для отримання блокування вам потрібен повнофункціональний об'єкт mutex, який, швидше за все, більший, ніж кілька біт. Щоб отримати підрахунок посилань ( ніеквівалент GC, і не повинен використовуватися як такий, але іноді корисний), вам потрібен розумний вказівник, який додає поле відліку посилань. Якщо об'єкт не сконструйований обережно, кількість посилань, об'єкт розумного вказівника та посилальний об'єкт знаходяться в абсолютно окремих місцях, і навіть коли ви правильно його сконструювали, спільний покажчик може (повинен бути) ще двома вказівниками замість одного. Знову ж таки, хороший стиль C ++ не використовує цих функцій для того, щоб мати значення - на практиці добре написані об’єкти бібліотеки C ++ використовують менше. Це не обов'язково означає менше споживання пам'яті в цілому, але це означає, що C ++ має гарний початок у цьому плані.

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


3

Простий int, в java, займає рівно стільки місця, скільки і intв C ++, за умови, що обидві реалізації використовують однаковий цілий розмір і вирівнювання пам'яті.

Int 'object' ( ціле коробки , тобто екземпляр класу Integer) несе всі накладні витрати екземпляра класу на Java, тому він значно більший, ніж intу C ++. Однак, якби ви оснащували об’єкт в C ++ тими ж можливостями, що і об’єкти Java, які випускаються поза вікном (поліморфізм, бокс, збирання сміття, RTTI), ви, ймовірно, отримаєте об'єкт, рівний розмір.

І тоді виникають міркування щодо оптимізації; оскільки моделі виконання та парадигми програмування відрізняються, навряд чи будь-яка нетривіальна проблема вирішиться однаково в обох мовах, тому порівняння розміру пам’яті на цьому рівні не має неймовірного сенсу.

Так, об’єкти Java за замовчуванням мають більше накладних витрат, ніж класи C ++, але вони мають більше функцій, і це призводить до іншого стилю програмування - хороший програміст може використовувати переваги і недоліки будь-якої мови.


+1 Більше накладних, але більше можливостей на Java, я зараз розумію, дякую
Ентоні
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.