Коли System.gc () щось робить?


118

Я знаю, що збирання сміття автоматизовано на Java. Але я зрозумів, що якщо ви вкажете System.gc()у своєму коді, то JVM може або не вирішить здійснити вивезення сміття в цей момент. Як це точно працює? На якій основі / параметрах саме JVM вирішує робити (або не робити) GC, коли бачитьSystem.gc() ?

Чи є приклади, в якому випадку корисно ввести це у свій код?


4
Занадто складна концепція, щоб детально описати тут, але ця стаття повинна вам допомогти: chaoticjava.com/posts/how-does-garbage-collection-work
GEOCHET

Точна поведінка залежить від СВМ, тобто від реалізації.
Thorbjørn Ravn Andersen

Відповіді:


59

На практиці це зазвичай вирішують займатися вивезенням сміття. Відповідь змінюється залежно від безлічі факторів, наприклад, у якому JVM ви працюєте, у якому режимі він перебуває та який алгоритм збору сміття використовується.

Я б не залежав від цього у вашому коді. Якщо JVM збирається викинути OutOfMemoryError, виклик System.gc () не зупинить це, оскільки збирач сміття спробує звільнити стільки, скільки зможе, перш ніж він перейде до цієї крайності. Єдиний раз, коли я його бачив на практиці, це IDE, де він прикріплений до кнопки, яку користувач може натиснути, але навіть там це не дуже корисно.


3
ви можете примусити збирання сміття в jconsole
Benedikt Waldvogel

25
@bene Чи можете ви насправді "змусити" це? Jconsole просто викликає System.gc ()?
Джон Мізер

4
Я б використовував System.gc()під час завантаження екран для відеоігри. У той момент мені було б зовсім не байдуже, чи дзвінок очистив усе, що можливо, або взагалі нічого не зробив. Однак я вважаю за краще, щоб це "витрачало зусилля на переробку невикористаних об'єктів" під час завантаження екрана, а не під час ігрового процесу.
Крьов

30

Єдиний приклад, з якого я можу подумати, де має сенс викликати System.gc (), коли профілює додаток для пошуку можливих витоків пам'яті. Я вважаю, що профілісти називають цей метод безпосередньо перед тим, як зробити знімок пам'яті.


4
Так, це я і роблю. Значення, повернені Runtime.freeMemory (), наприклад, мають справжнє значення лише після System.gc (), тому що, як правило, ви хочете знати, скільки пам'яті блокується непридатними екземплярами. Використовується лише для налагодження / профілювання пам'яті, звичайно, ніколи у виробництві.
sleske

Ні, це добре і для багатьох інших сценаріїв. Наприклад, скажімо, що у вас є єдиний зразок, який усвідомлює життєвий цикл. У onStop (), якщо ви є null (ing) екземпляром, добре б зателефонувати System.gc (), щоб допомогти йому.
portfoliobuilder

26

Специфікація мови Java не гарантує, що JVM запустить GC під час дзвінка System.gc(). Це є причиною цього "може або не може прийняти рішення про те, щоб зробити GC в цій точці".

Тепер, якщо ви подивитесь на вихідний код OpenJDK , який є основою OV JVM, ви побачите, що виклик System.gc()дійсно починає цикл GC. Якщо ви використовуєте інший JVM, такий як J9, вам слід перевірити їх документацію, щоб дізнатися відповідь. Наприклад, JVM Azul має сміттєзбірник, який працює постійно, тому дзвінок наSystem.gc() нічого не робити

Інша відповідь згадує запуск GC в JConsole або VisualVM. В основному ці інструменти здійснюють віддалений дзвінок на System.gc().

Зазвичай ви не хочете починати цикл вивезення сміття зі свого коду, оскільки це псується з семантикою вашої програми. Ваша програма робить деякі ділові речі, JVM піклується про управління пам'яттю. Ви повинні тримати ці проблеми окремо (не змушуйте вашу програму займатися деяким управлінням пам'яттю, не зосереджуватися на бізнесі).

Однак, мало випадків, коли заклик System.gc()може бути зрозумілим. Розглянемо, наприклад, мікро-орієнтири. Ніхто не хоче, щоб цикл GC стався посеред мікро-орієнтиру. Таким чином, ви можете запустити цикл GC між кожним вимірюванням, щоб переконатися, що кожне вимірювання починається з порожньої купи.


26

У вас немає контролю над GC в java - вирішує ВМ. Я ніколи не стикався з випадками, коли System.gc()це потрібно . Оскільки System.gc()заклик просто ЗАПРОШУЄТЬСЯ про те, що VM робить сміттєзбір, а також робить ПОПУСКУВАННЯ сміття (старі та нові покоління в купі багато поколінь), то це може насправді спричинити БІЛЬШЕ циклів центрального процесора для споживання , ніж це необхідно.

У деяких випадках, можливо, буде доцільно запропонувати ВМ, щоб вона робила повну колекцію ЗАРАЗ, оскільки, можливо, ви знаєте, що програма буде сидіти в режимі очікування протягом наступних кількох хвилин до того, як відбудеться важке підняття. Наприклад, одразу після ініціалізації багатьох тимчасових об'єктів під час запуску програми (тобто я просто кешував TON інформації, і я знаю, що не буду отримувати великої активності ні на хвилину або близько того). Подумайте про такий IDE, як запуску затемнення - він робить багато для ініціалізації, тому, можливо, відразу після ініціалізації має сенс зробити повний gc в цей момент.


17

Вам потрібно бути дуже обережним, якщо ви телефонуєте System.gc(). Зателефонувавши до нього, можна додавати зайві проблеми з вашою програмою, і фактично виконати колекцію не гарантовано. Фактично можна відключити явне System.gc()через аргумент java -XX:+DisableExplicitGC.

Я настійно рекомендую ознайомитись з документами, доступними в Java HotSpot Garbage Collection, щоб отримати більш детальну інформацію про збирання сміття.


Мій додаток для Android завжди кидає мені OutOfMemoryException. Тому я думав використовувати System.gc () у функції onDestroy мого класу, щоб очистити всі небажані посилання та об’єкти. Чи гарний підхід
Сагар Деванга

2
@Sagar Devanga Ручне виклик gc, як ви описуєте, навряд чи допоможе. Перед тим, як кинути МОМ, JVM вже буде намагатися збирати сміття, щоб звільнити пам'ять
Scrubbie

10

System.gc()реалізований VM, і те, що він робить, - це конкретна реалізація. Наприклад, реалізатор може просто повернутися і нічого не робити.

Що стосується того, коли видати ручну колекцію, єдиний час, коли ви можете це зробити, це коли ви відмовитесь від великої колекції, що містить набір менших колекцій - Map<String,<LinkedList>> наприклад, - і ви хочете спробувати скористатись хітом Perf, а потім там, але здебільшого вам не варто турбуватися про це. ГК знає краще за вас - на жаль - більшість часу.


8

Якщо ви використовуєте буфери прямої пам’яті, JVM не запускає GC для вас, навіть якщо у вас є низька кількість прямої пам'яті.

Якщо ви телефонуєте ByteBuffer.allocateDirect()і отримуєте OutOfMemoryError, ви можете знайти цей виклик добре після запуску GC вручну.


Ви хочете сказати, що System.gc () збирає прямі асигнування, тоді як JVM, як правило, не (перед тим, як кинути OOM). Тому що я думаю, що це неправильно. Єдиною причиною, що розподіл може відбутись після експліцитного GC, - це додатковий час та деяка більша ймовірність появи об'єкта в купі, коли фіналізатор буде звільнений (та звільнення деякого безпосередньо виділеного об'єкта).
eckes

В останній версії Java 6 код у allocateBuffer завжди запускатиме System.gc () перед тим, як викинути OutOfMemoryError. У фіналізаційному коді він є коротким скороченням, для Cleanersякого використовується пряма пам'ять. тобто вона не звільняється фіналізатором, оскільки це може бути занадто повільним. Незважаючи на це, можна отримати OOME, якщо ви особливо швидко створюєте регіони прямої пам'яті. Рішення полягає в тому, щоб цього не робити і повторно використовувати свої області прямої пам’яті, де можна.
Пітер Лорі

1
дякую, це звучить більше як мій досвід. Тому я думаю, що відповідь правильна лише для старих версій Java.
eckes

5

Більшість JVM запускають GC (залежно від -XX: DiableExplicitGC та -XX: + ExplicitGCInvokesConcurrent switch). Але специфікація лише менш чітко визначена, щоб згодом забезпечити кращі реалізації.

Специфікація потребує уточнення: Bug # 6668279: (spec) System.gc () має вказувати, що ми не рекомендуємо використовувати та не гарантуємо поведінку

Внутрішньо метод gc використовується RMI та NIO, і вони вимагають синхронного виконання, що: це зараз обговорюється:

Помилка № 5025281: Дозволити System.gc () запускати одночасні (не зупинятись у світі) повні колекції


3

Garbage Collectionце добре Java, якщо ми виконуємо програмне забезпечення, кодоване в Java на робочому столі / ноутбуці / сервері. Ви можете зателефонувати System.gc()або Runtime.getRuntime().gc()ввійти Java.

Зауважте лише, що жоден із цих дзвінків не гарантує нічого. Вони є лише пропозицією для jvm запустити сміттєзбірник. Це до JVM, чи працює він GC чи ні. Отже, коротка відповідь: ми не знаємо, коли це працює. Більш довга відповідь: JVM запустить gc, якщо на це буде час.

Я вважаю, те саме стосується і Android. Однак це може уповільнити вашу систему.


JVM запускає gc, якщо на це є час : не завжди правда, Jvm може запускати gc, коли пам'ять Eden заповнена.
abdelmouheimen

2

Зазвичай, VM здійснює збір сміття автоматично перед тим, як викинути OutOfMemoryException, тому додавання явного виклику не повинно допомогти, за винятком того, що воно, можливо, переміщує показник продуктивності на більш ранній момент.

Однак, я думаю, я зіткнувся з випадком, коли це може бути актуальним. Я не впевнений, оскільки ще не перевіряв, чи має це вплив:

Коли ви пам'ятаєте файл на карті, я вважаю, що виклик map () кидає IOException, коли недостатньо великий блок пам'яті недоступний. Я думаю, що збирання сміття безпосередньо перед файлом map () може запобігти цьому. Що ти думаєш?


2

ми ніколи не можемо примусити збирання сміття. System.gc пропонує лише vm для вивезення сміття, однак, дійсно, у який час працює механізм, ніхто не знає, це як зазначено в специфікаціях JSR.


2

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

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

Отже, єдиний раз, коли моя програма викликає System.gc (), це коли вона збирається ввести сегмент коду, де будуть виділятися великі буфери, необхідні для обробки даних, і тест на вільну пам'ять, що наявна, вказує на те, що я не гарантовано мати його. Зокрема, я дивлюсь на 1 Гбіт середовище, де щонайменше 300 Мб зайнято статичними даними, при цьому основна частина нестатичних даних пов'язана з виконанням, за винятком випадків, коли оброблювані дані мають становити принаймні 100-200 Мб джерело. Це все є частиною автоматичного перетворення даних, тому ці дані існують протягом відносно короткого періоду часу.

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

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


2

Якщо ви хочете дізнатися, чи System.gc()викликається ваш телефон, ви можете з новим оновленням Java 7 отримати повідомлення, коли JVM виконує збір сміття.

Я не на 100% впевнений, що клас GarbageCollectorMXBean вводить в оновлення Java 7 4, тому що я не міг знайти його в примітках до випуску, але я знайшов інформацію на сайті javaperformancetuning .com


0

Я не можу придумати конкретного прикладу, коли добре запустити явний GC.

Взагалі, запуск явного GC може насправді заподіяти більше шкоди, ніж користі, оскільки явний gc спричинить повну колекцію, яка забирає значно більше часу, коли вона проходить через кожен об'єкт. Якщо цей явний gc закінчується повторним викликом, це може легко призвести до повільного застосування, оскільки багато часу витрачається на виконання повних ГК.

Крім того, якщо ви переходите за купою з аналізатором купи і ви підозрюєте, що компонент бібліотеки викликає явні GC, ви можете відключити додавання: gc = -XX: + DisableExplicitGC до параметрів JVM.


2
Ми закликаємо System.gc (), щоб спробувати закрити наші об'єкти MappedByteBuffer, які в іншому випадку не закриті, а тому не доступні для інших процесів або для усічення файлів, навіть якщо ми називаємо "close ()" на об'єктах. На жаль, це все ще не завжди їх закриває.
Тім Купер

0

Окремий випадок використання для явного виклику System.gc () - це те, що ви хочете примусити доопрацювати, тобто метод виклику для завершення .


Примітка: існує власний метод змусити виконати завершення. (Проблема насправді не в запуску фіналізаторів, а в визнанні того, що об’єкт може бути доопрацьований, оскільки він не визначений)
eckes

-1

поки system.gc працює, він зупинить світ: всі відповіді зупинені, щоб сміттєзбірник міг сканувати кожен об’єкт і перевірити, чи потрібно його видалити. якщо додаток є веб-проектом, всі запити зупиняються, поки gc не закінчиться, і це призведе до того, що ваш веб-проект не може працювати в грошовій формі.

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