Якщо Java збирає сміття, то чому виникає OutOfMemoryError?

Це теоретичне питання. Мені просто цікаво, що якщо Java має механізм збирання сміття для звільнення пам'яті, то чому все ще відбувається OutOfMemoryError ?

Я шукав SO для цього, але міг отримати ці посилання

Чи мусорний колектор гарантований для запуску раніше Помилка пам'яті?

Збирання сміття перед OutOfMemoryError

Вони не відповідають моєму запиту конкретно. Якщо Java дозволяє пам'яті настільки добре, використовуючи збирання сміття, то чому відбувається OutOfMemoryError ?

7
@ПрасадХаркар, тільки одна прийнята відповідь. Якщо всі вони однаково добре, то я приймаю першого, хто відповість, і віддаю перевагу іншим.
додано Автор earcam, джерело
Якщо ви не покладете свою рубашку назовні, колекціонери не зможуть його відняти. Те ж саме з Java, якщо з якоїсь причини ви звільняєте посилання на свої об'єкти, їх не збиратиме збирач сміття. Це називається витік пам'яті.
додано Автор Luis, джерело
Збір сміття не означає, що у вас є нескінченна пам'ять.
додано Автор Louis Wasserman, джерело
Так я зробив те ж саме :)
додано Автор Prasad Kharkar, джерело
Чи дозволяє SO прийняти лише одну відповідь? Я намагаюся прийняти все, крім того, що вибирається
додано Автор Prasad Kharkar, джерело

7 Відповіді

OutOfMemoryError може виникнути, якщо жоден із об'єктів пам'яті не підходить для збирання сміття. Наприклад:

List hellos = new ArrayList<>();
for (;;) {
   hellos.add(new MyClass());
}

Це створює список і зберігає додавання об'єктів MyClass до моменту закінчення пам'яті. Жоден з об'єктів не може брати участь у зборі сміття, оскільки є посилання на всі їх.

12
додано
@PrasadKharkar - Вам навіть не потрібна петля! Просто спробуйте виділити один ОЧИЩЕНОГО масиву.
додано Автор Stephen C, джерело
@PrasadKharkar - Вам не потрібна нескінченна петля. Це просто приклад. Все, що вам потрібно, це проблема, коли кількість доступних даних у обчисленні занадто велика для купи ... протягом певного часу під час обчислення.
додано Автор Stephen C, джерело
З мого розуму я зовсім вислизнув, що нескінченний цикл може спричинити створення нескінченних об'єктів, і всі вони мають посилання зі стеків. Дякую, сер, що це усуває мої сумніви
додано Автор Prasad Kharkar, джерело
@StephenC, для виконання OutOfMemoryError цикл потрібно просто запустити досить довго, щоб зайняти весь об'єм пам'яті, чи не так? Немає необхідності в нескінченному циклі
додано Автор Prasad Kharkar, джерело
Так, зрозуміло це зараз :) Дякую
додано Автор Prasad Kharkar, джерело

Також можна закріпити пам'ять тим, чого ви не очікуєте. Розглянемо наступний рядок-приклад.

char[] chars      = new char[10_000_000]; //May need to adjust.
String string     = new String(chars);
chars             = null;
String substring  = string.substring(5_000_000);
string            = null; 

Зберігати масив chars . Масив всередині string не може бути зібраний, оскільки substring містить посилання на внутрішній масив, з яким слідує зміщення та діапазон. Таким чином, 10 7 символів залишаються виділеними, хоча використовуються і доступні лише 5 * 10 6 .

Java 1.7.0_06

Здається, що String.substring більше не має такої поведінки. У статті в веб-сайті керівництва з налаштування продуктивності Java , Михайло Воронцов повідомляє, що в Java 1.7.0_06 і вище String.substring завжди створює новий String , незалежно від старого. Клас String більше не має змінних екземпляра offset та range . Створення великого рядка, взяття підрядків та відкидання оригіналу не залишить позачерговий рядок char [] старої струни.

// Java 1.7.0_06 and up
char[] chars      = new char[10_000_000]; //May need to adjust.
String string     = new String(chars);
chars             = null;
String substring  = string.substring(5_000_000);
// Copies a portion of string's array to a new array.
string            = null;
// string's array is no longer reachable, and may be garbage collected.
3
додано
тут, "рядок" буде доступний у пам'яті, але воно не буде переадресовано, чи не так? Це відбувається тому, що ми виконуємо операцію на рядковому об'єкті, оскільки вона незмінна, буде створено новий рядок, а попередній буде втрачено
додано Автор Prasad Kharkar, джерело
спасибі за пояснення :)
додано Автор Prasad Kharkar, джерело
Ні, оскільки string незмінна, не буде ніяких дій для зміни його вмісту. Таким чином, для JVM безпечно використовувати substring повторно використовувати char [] всередині string . Тоді, коли для параметра string встановлено значення null, усі змінні в string можуть бути зібрані, за винятком самого масиву; substring відноситься до нього.
додано Автор Eric Jablow, джерело

Тому що не вся пам'ять - це сміття.

2
додано
Це правильно. І якщо ви продовжувати запитувати все більше і більше пам'яті, які може бути посиланнями на програму, ви закінчите.
додано Автор EJP, джерело
З мого розуміння, пам'ять сміття - це пам'ять, зайнята об'єктами, на які більше не можна звертатися з програми. Я правий?
додано Автор Prasad Kharkar, джерело
Так, отримав це :) дякую вам, пане
додано Автор Prasad Kharkar, джерело

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

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

1
додано
Ох, так, як це зникло з розуму, що створення об'єкта в нескінченному циклі може створити кілька об'єктів, і вони матимуть посилання з стека. Спасибі @cHao, це допомогло мені :)
додано Автор Prasad Kharkar, джерело

Сміттєвий збирач працює з пам'яттю, який не буде використовуватися.

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

Коли виділена пам'ять занадто висока над дозволеною для віртуальної машини, ви отримуєте це виключення.

1
додано

Якщо ви вичерпаєте пам'ять і немає змінної або об'єкта, що підлягає видаленню, виключення буде підвищено.

Тому найкращим способом використання GC (Garbage collection) є:

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

0
додано

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

0
додано
Це не так просто. Що робити, якщо декілька посилань стосуються того самого об'єкта. У цьому випадку, якщо ви знівете об'єкт, то є шанс зависання посилання !!! Отже, знімінити об'єкт не завжди !!!
додано Автор Narendra, джерело
Налаштування посилання на об'єкт null зазвичай є повною марною тратою часу. У всякому разі, метод-локальні посилання випадає з сфери застосування, і посилання, які є змінні-екземплярами, не так легко встановити на нуль у довільному режимі, якщо вони дійсно не повинні бути локальними змінами, і в цьому випадку вони все-таки вийдуть з сфери застосування ...
додано Автор EJP, джерело
ІТ КПІ - Java
ІТ КПІ - Java
436 учасників