Сегодня наша тема посвящена утечкам памяти внутри Java. Подожди!? Утечки памяти в Java? Это ведь невозможно, верно? Разве это не одно из обещаний Java – иметь управляемую память? Когда я начал учиться программировать, я начал с C++, как люди. Позвольте мне сказать вам кое-что, если вы плохо управляете своей памятью с++, вы, скорее всего, оторвете себе ногу. Но в любом случае мы говорили об утечках памяти в Java. Я думаю, что лучший способ показать это на примере, и я думаю, что пример из книги – отличный пример этого:
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_SIZE = 16;
public Stack() {
elements = new Object[DEFAULT_INITIAL_SIZE];
}
public void push(Object element) {
ensureCapacity();
elements[size++] = element;
}
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
return elements[--size];
}
private void ensureCapacity() {
if(elements.length = size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}
Похоже, это сработает правильно? Я открою тебе один секрет. Этот код действительно работает нормально но у него действительно есть “утечка памяти”. Вы можете это определить? Я, например, определенно не понял этого сразу. Проблема заключается в этой строке здесь из нашей функции pop .:
return elements[--size];
Так что же не так с этой строкой? Концептуально код возвращает объект и удаляет его из стека. На самом деле происходит то, что массив elements все еще имеет ссылку на объект и не сможет быть собран как мусор, пока этот элемент не будет перезаписан. Поэтому, если, например, в стек была добавлена куча объектов, а затем удалена, мы ожидали бы, что элементы будут собраны в мусор, но они этого не сделают.
Но действительно ли это утечка памяти? Не в обычном смысле этого слова. Более правильно их можно было бы назвать “устаревшие ссылки на объекты. “Это, как говорится, симптомы и проблемы, которые это может вызвать, находятся в одном и том же направлении. Итак, как нам решить эту проблему? Ну достаточно просто мы обнуляем объект перед возвращением:
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
Object poppedObject = elements[--size];
elements[size] = null;
return poppedObject;
}
Итак, я говорю, что вы должны обнулить все объекты после того, как закончите с этим? Пожалуйста, нет. Необходимость обнулять объекты почти всегда является исключением, а не правилом. Java действительно является языком с управляемой памятью, и он будет обрабатывать очистку наших объектов, когда они выпадают из области видимости и теряют все ссылки на них. Итак, в каких случаях нам нужно учитывать вышеуказанную проблему, а когда нет? Все сводится к тому, управляем ли мы вашей собственной памятью/объектами или нет. Если вы управляете своей памятью, вам нужно пройти весь путь и управлять своей памятью, а не просто делать это наполовину. Выше мы сами управляем нашей памятью, поскольку у нас есть массив элементов, которыми мы управляем как хранилищем.
В каких еще местах мы это видим? Кэши – еще один пример. Нам нужно знать о жизненном цикле наших данных в наших кэшах, когда мы используем их, поскольку наш кэш может предотвратить сбор мусора для объекта, даже если ничто другое в системе никогда не запросит объект из кэша. Один из способов объяснить это – использовать класс Java WeakReference . Нам действительно нужно знать, что при использовании этого класса он является просто указателем на объект и рассчитывает на то, что внешняя система, имеющая ссылку на объект, не позволит ему быть собранным мусором, пока он все еще пригоден для использования. Использование слабых ссылок и связанных классов – более продвинутая тема, и то, как ее разумно использовать, выходит за рамки этой статьи в блоге. Последнее местоположение, которое разработчики могут оставить вокруг устаревших объектов, – это при использовании прослушивателей и других обратных вызовов. Я помню, что это вызывало особую озабоченность при работе с Android в моей предыдущей жизни. Это можно объяснить тем же способом, что и выше, с использованием Слабых ссылок
Тема сегодняшнего обсуждения, без сомнения, немного более узкая. Скорее всего, вы не будете сталкиваться с этим каждый день но это одна из вещей, которую следует иметь в виду при разработке нашего программного обеспечения. Утечки памяти чрезвычайно трудно отлаживать, и поэтому мы должны сделать все, что в наших силах, чтобы избежать их.
Оригинал: “https://dev.to/kylec32/effective-java-tuesday-don-t-leak-object-references-1dd9”