Це давня скарга на Java, але це в значній мірі безглуздо і, як правило, ґрунтується на перегляді невірної інформації. Звичайна фраза - це щось на кшталт "Hello World на Java займає 10 мегабайт! Навіщо це потрібно?" Ну ось ось спосіб змусити Hello World на 64-розрядному JVM претендувати на придбання 4 гігабайт ... принаймні за однією формою вимірювання.
java -Xms1024m -Xmx4096m com.example.Hello
Різні способи вимірювання пам'яті
У Linux верхня команда дає кілька різних номерів для пам'яті. Ось що йдеться про приклад Hello World:
PID USER PR NI VIRT RES SHR S% CPU% MEM TIME + COMMAND
2120 кгрегорі 20 0 4373м 15м 7152 S 0 0,2 0: 00,10 ява
- VIRT - це віртуальний простір пам'яті: сума всього, що знаходиться на карті віртуальної пам'яті (див. Нижче). Це в значній мірі безглуздо, за винятком випадків, коли цього немає (див. Нижче).
- RES - це розмір набору резидентів: кількість сторінок, які наразі знаходяться в оперативній пам'яті. Майже у всіх випадках це єдине число, яке слід використовувати, кажучи "занадто великий". Але це все-таки не дуже вдале число, особливо якщо говорити про Java.
- SHR - це об'єм пам'яті резидента, який ділиться з іншими процесами. Для Java-процесу це, як правило, обмежене спільними бібліотеками та JAR-файлами, відображеними на пам'ять. У цьому прикладі у мене працював лише один Java-процес, тому я підозрюю, що 7k - результат бібліотек, використовуваних ОС.
- SWAP не увімкнено за замовчуванням і тут не відображається. Він вказує кількість віртуальної пам’яті, яка наразі знаходиться на диску, незалежно від того, перебуває вона фактично у просторі підкачки . ОС дуже добре зберігає активні сторінки в оперативній пам’яті, і єдиними засобами для заміни є (1) придбати більше пам’яті або (2) зменшити кількість процесів, тому найкраще ігнорувати це число.
Ситуація для Windows Task Manager трохи складніша. У Windows XP є стовпці «Використання пам’яті» та «Розмір віртуальної пам’яті», однак офіційна документація замовчує, що вони означають. Windows Vista та Windows 7 додають більше стовпців, і вони фактично задокументовані . З них найбільш корисним є вимірювання «Робочого набору»; це приблизно відповідає сумі ВДЕ та ЗГР в Linux.
Розуміння карти віртуальної пам'яті
Віртуальна пам'ять, яка споживається процесом, - це загальна кількість всього, що є в карті пам'яті процесу. Сюди входять дані (наприклад, купа Java), а також усі спільні бібліотеки та файли, відображені в пам'яті, які використовує програма. В Linux ви можете використовувати команду pmap, щоб побачити всі речі, відображені в просторі процесу (з цього моменту я збираюся посилатися лише на Linux, тому що це я використовую; я впевнений, що є рівноцінні інструменти для Windows). Ось уривок із карти пам’яті програми «Hello World»; вся карта пам’яті завдовжки понад 100 рядків, і це не незвично, щоб мати список тисяч рядків.
0000000040000000 36K rx-- /usr/local/java/jdk-1.6-x64/bin/java
0000000040108000 8K rwx-- /usr/local/java/jdk-1.6-x64/bin/java
0000000040eba000 676K rwx-- [anon]
00000006fae00000 21248K rwx-- [anon]
00000006fc2c0000 62720K rwx-- [anon]
0000000700000000 699072K rwx-- [anon]
000000072aab0000 2097152K rwx-- [anon]
00000007aaab0000 349504K rwx-- [anon]
00000007c0000000 1048576K rwx-- [anon]
...
00007fa1ed00d000 1652K r-xs- /usr/local/java/jdk-1.6-x64/jre/lib/rt.jar
...
00007fa1ed1d3000 1024K rwx-- [anon]
00007fa1ed2d3000 4K ----- [anon]
00007fa1ed2d4000 1024K rwx-- [anon]
00007fa1ed3d4000 4K ----- [anon]
...
00007fa1f20d3000 164K rx-- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
00007fa1f20fc000 1020K ----- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
00007fa1f21fb000 28K rwx-- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
...
00007fa1f34aa000 1576K rx-- /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3634000 2044K ----- /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3833000 16K rx-- /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3837000 4K rwx-- /lib/x86_64-linux-gnu/libc-2.13.so
...
Швидке пояснення формату: кожен рядок починається з адреси віртуальної пам'яті сегмента. Після цього розмір сегмента, дозволи та джерело сегмента. Останній елемент - це або файл, або "anon", що вказує на блок пам'яті, виділений через mmap .
Починаючи зверху, маємо
- Навантажувач JVM (тобто програма, яка запускається під час введення тексту
java
). Це дуже мало; все, що він робить - це завантаження у спільних бібліотеках, де зберігається реальний код JVM.
- Купа анон-блоків, що містять кучу Java та внутрішні дані. Це Sun JVM, тому купа розбивається на кілька поколінь, кожне з яких є власним блоком пам'яті. Зауважте, що JVM виділяє віртуальний простір пам'яті на основі
-Xmx
значення; це дозволяє йому мати суміжну купу. -Xms
Величина використовується , щоб сказати , скільки з купи «використовується» при запуску програми, а також збір сміття спускового як межа наближення.
- JARfile, зібраний на пам'ять, у цьому випадку файл, що містить "класи JDK". Коли ви пам'ятаєте JAR-карту, ви можете дуже ефективно отримувати доступ до файлів, що знаходяться в ній (проти читання з початку). Sun JVM запам'ятовує карту пам'яті всіх JAR на класному шляху; якщо вашому коду програми потрібно отримати доступ до JAR, ви також можете зробити карту пам'яті.
- Дані на нитку для двох потоків. Блок 1М - це стек потоків. Я не мав хорошого пояснення для блоку 4k, але @ericsoe визначив його "захисним блоком": він не має дозволу читання / запису, тому він може отримати помилку сегменту при доступі, і JVM вловлює це і перекладає це до а
StackOverFlowError
. У реальному додатку ви побачите десятки, якщо не сотні цих записів, повторених через карту пам'яті.
- Одна з спільних бібліотек, що містить фактичний код JVM. Є кілька таких.
- Спільна бібліотека для стандартної бібліотеки С. Це лише одна з багатьох речей, які завантажує JVM, які не є строго частиною Java.
Загальнодоступні бібліотеки особливо цікаві: кожна бібліотека, що ділиться, має щонайменше два сегменти: сегмент, що містить лише читання, що містить код бібліотеки, і сегмент для читання-запису, який містить глобальні дані про процес для бібліотеки (я не знаю, що сегмент без дозволів є; я бачив його лише на x64 Linux). Частину бібліотеки лише для читання можна розділити між усіма процесами, які використовують бібліотеку; наприклад, libc
має 1,5 мільйона віртуального простору пам'яті, яким можна ділитися.
Коли важливий розмір віртуальної пам'яті?
Карта віртуальної пам'яті містить багато матеріалів. Деякі з них доступні лише для читання, деякі - загальним, а частина виділяється, але ніколи не торкається (наприклад, майже весь 4Gb купи в цьому прикладі). Але операційна система досить розумна, щоб завантажувати лише те, що їй потрібно, тому розмір віртуальної пам'яті значною мірою не має значення.
Там, де важливий розмір віртуальної пам'яті, це якщо ви працюєте в 32-бітній операційній системі, де ви можете виділити лише 2 Гб (або, в деяких випадках, 3 Гб) адресного простору процесу. У такому випадку ви маєте справу з дефіцитним ресурсом, і, можливо, доведеться робити компроміси, такі як зменшення розміру вашої купи, щоб зробити карту пам'яті великим файлом або створити багато потоків.
Але, враховуючи, що 64-розрядні машини є всюдисущими, я не думаю, що це буде задовго до того, як розмір віртуальної пам'яті стане абсолютно нерелевантною статистикою.
Коли важливий розмір резидента?
Resident Set розмір - це частина простору віртуальної пам'яті, яка фактично знаходиться в оперативній пам'яті. Якщо ваш RSS стане значною частиною вашої загальної фізичної пам’яті, можливо, прийшов час почати хвилюватися. Якщо ваш RSS зростає, займаючи всю вашу фізичну пам’ять, і ваша система починає мінятися, вже минув час починати хвилюватися.
Але RSS також вводить в оману, особливо на слабо завантаженій машині. Операційна система не витрачає багато зусиль на повернення сторінок, які використовуються в процесі. Користуючись цим, мало користі, і потенціал для дорогої помилки сторінки, якщо процес торкнеться сторінки в майбутньому. Як результат, статистика RSS може містити багато сторінок, які не використовуються.
Нижня лінія
Якщо ви не поміняєте місцями, не переймайтеся надто великою статистикою пам'яті. З застереженням, що постійно зростаючий RSS може вказувати на якусь витік пам'яті.
З програмою Java набагато важливіше звернути увагу на те, що відбувається в купі. Загальна кількість споживаного місця важлива, і є кілька кроків, які можна вжити, щоб зменшити це. Більш важливим є кількість часу, який ви проводите на збирання сміття, і які частини купи збираєте.
Доступ до диска (тобто до бази даних) коштує дорого, а пам'ять - недорого. Якщо ви можете торгувати один за іншим, зробіть це.