Looking for java Keywords? Try Ask4Keywords

Java Language Pitfall: утечки памяти


пример

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

Тем не менее, вы можете предотвратить освобождение памяти, позволяя объектам быть доступными, которые больше не нужны. Если вы называете это утечкой памяти или упаковкой памяти, результат будет таким же - ненужное увеличение выделенной памяти.

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

Статические поля

Можно создать такую ​​ссылку путем определения класса со static полем, содержащим некоторую коллекцию объектов, и забыть установить это static поле в null после того, как сбор больше не нужен. static поля считаются корнями GC и никогда не собираются. Другой проблемой является утечка в памяти без кучи при использовании JNI .

Утечка класса загрузчика

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

Утечка утечки. Пример утечки утечки может выглядеть следующим образом:

final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
final Deque<BigDecimal> numbers = new LinkedBlockingDeque<>();
final BigDecimal divisor = new BigDecimal(51);

scheduledExecutorService.scheduleAtFixedRate(() -> {
    BigDecimal number = numbers.peekLast();
    if (number != null && number.remainder(divisor).byteValue() == 0) {
        System.out.println("Number: " + number);
        System.out.println("Deque size: " + numbers.size());
    }
}, 10, 10, TimeUnit.MILLISECONDS);

scheduledExecutorService.scheduleAtFixedRate(() -> {
    numbers.add(new BigDecimal(System.currentTimeMillis()));
}, 10, 10, TimeUnit.MILLISECONDS);

try {
    scheduledExecutorService.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
    e.printStackTrace();
}

В этом примере создаются две запланированные задачи. Первая задача берет последнее число из дека, называемого numbers , и, если число делится на 51, оно печатает число и размер дека. Вторая задача помещает числа в deque. Обе задачи запланированы с фиксированной скоростью, и они запускаются каждые 10 мс.

Если код выполнен, вы увидите, что размер deque постоянно увеличивается. Это в конечном итоге приведет к тому, что deque будет заполнено объектами, которые потребляют всю доступную память кучи.

Чтобы предотвратить это при сохранении семантики этой программы, мы можем использовать другой метод для pollLast чисел из deque: pollLast . В отличие от метода peekLast , pollLast возвращает элемент и удаляет его из deque, в то время как peekLast возвращает только последний элемент.