Как предотвратить сбор объекта из мусора?
Есть ли какие-либо подходы путем завершения или ссылки phantom или любых других подходов?
Мне задали этот вопрос в интервью. Интервьюер предположил, что finalize()
можно использовать.
Как предотвратить сбор объекта из мусора?
Есть ли какие-либо подходы путем завершения или ссылки phantom или любых других подходов?
Мне задали этот вопрос в интервью. Интервьюер предположил, что finalize()
можно использовать.
Держите ссылку. Если ваш объект собирается преждевременно, это является признаком того, что у вас есть ошибка в дизайне вашего приложения.
Сборщик мусора собирает только объекты, к которым в вашем приложении нет ссылки. Если нет объекта, который, естественно, ссылался бы на собранный объект, спросите себя, почему он должен оставаться в живых.
Одна пользовательская система, в которой вы обычно не имеете ссылок, но хотите, чтобы объект был одноэлементным. В этом случае вы можете использовать статическую переменную. Одна из возможных реализаций синглтона будет выглядеть так:
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqInstance;
}
}
Изменить: Технически вы можете сохранить ссылку где-нибудь в своем финализаторе. Это предотвратит сбор объекта до тех пор, пока коллекционер не определит, что ссылок больше нет. Однако финализатор будет вызываться не чаще одного раза, поэтому вы должны убедиться, что ваш объект (включая его суперклассы) не нужно дорабатывать после первой коллекции. Однако я бы посоветовал вам не использовать эту технику в реальных программах. (Он оставит таких коллег, как я, крича WTF!?;)
protected void finalize() throws Throwable {
MyObjectStore.getInstance().store(this);
super.finalize(); // questionable, but you should ensure calling it somewhere.
}
Ответ на трюк, который ваш собеседник искал, вероятно, заключается в том, что он хочет, чтобы вы знали, что вы можете предотвратить сбор мусора от удаления объекта, вызывая утечку памяти.
Очевидно, что если вы сохраните ссылку на объект в каком-то долгоживущем контексте, он не будет собран, но это не то, о чем спрашивал рекрутер OP. Это не то, что происходит в методе finalize.
Что вы можете сделать, чтобы предотвратить сбор мусора из метода finalize, это написать бесконечный цикл, в котором вы вызываете Thread.yield();
(предположительно, чтобы сохранить пустую петлю от оптимизации):
@Override
protected void finalize() throws Throwable {
while (true) {
Thread.yield();
}
}
Моя ссылка здесь - статья Elliot Back, в которой описывается принудительное утечка памяти этим методом.
Еще один способ, с помощью которого завершать методы - зло..
Если есть еще ссылка на объект, он не получит сбор мусора. Если ссылок на него нет, вам все равно.
Другими словами - сборщик мусора собирает мусор. Пусть он выполняет свою работу.
Лучше всего использовать Unsafe, хотя ByteBuffer
может быть возможным обходным путем для некоторых случаев.
Также найдите ключевое слово "off-heap" memory.
Опасное
Преимущества по сравнению с ByteBuffer
:
Нелегко работать. Метод описан в следующих статьях:
Все они состоят из следующих шагов:
нам нужен оператор sizeof
, который у Unsafe не имеет. Как сделать один из них был задан по адресу: В Java, каков наилучший способ определить размер объекта?. Лучшие варианты, скорее всего, это API instrument
, но для этого требуется создать Jar и использовать специальные параметры командной строки...
когда у нас есть sizeof
, выделите достаточно памяти с помощью Unsafe#allocateMemory
, которая в основном является malloc
и возвращает адрес
создать регулярный объект кучи, скопировать его в выделенную память с помощью Unsafe#copyMemory
. Для этого вам необходимо указать адрес объекта on-heap и размер объекта
установите Object
, чтобы указать на выделенную память, затем введите Object
в свой класс.
Невозможно напрямую установить адрес переменной с помощью Unsafe, поэтому нам нужно обернуть объект в массив или объект-оболочку и использовать Unsafe#arrayBaseOffset
или Unsafe#objectFieldOffset
.
как только вы закончите, освободите выделенную память с помощью freeMemory
Если я когда-нибудь получу это, чтобы не выполнить segfault, я отправлю пример: -)
ByteBuffer
Преимущества по сравнению с небезопасными:
Содержимое прямых буферов может находиться вне обычной мусорной корзины.
Пример использования с примитивами:
ByteBuffer bb = ByteBuffer.allocateDirect(8);
bb.putInt(0, 1);
bb.putInt(4, 2);
assert bb.getInt(0) == 1;
assert bb.getInt(4) == 2;
// Bound chekcs are done.
boolean fail = false;
try {
bb.getInt(8);
} catch(IndexOutOfBoundsException e) {
fail = true;
}
assert fail;
Связанные темы:
Я подозреваю, что вы можете иметь в виду, если ваш метод finalize
задерживает ссылку на завершаемый объект. В этом случае (если мое чтение Java Language Spec верное), метод finalize
никогда не будет повторно запущен, но объект пока не будет собран мусор.
Это не та вещь, которая есть в реальной жизни, кроме, возможно, случайно!
Это звучит как одно из тех вопросов, которые вы можете задать только по запросу. finalize() запускается, когда ваш объект получает сбор мусора, поэтому было бы довольно извращенным положить что-то там, чтобы предотвратить сбор. Обычно вы просто держите ссылку и все, что вам нужно.
Я даже не уверен, что произойдет, если вы создадите новую ссылку для чего-то в финализаторе - поскольку сборщик мусора уже решил собрать его, вы в итоге получите нулевой ref? В любом случае это плохая идея. например
public class Foo {
static Foo reference;
...
finalize (){
reference = this;
}
}
Я сомневаюсь, что это сработает, или оно может работать, но зависит от имплантации GC или быть "неопределенным поведением". Однако выглядит злым.
Ключевым моментом является установка реальной ссылочной переменной, указывающей на объект null, хотя у нас есть переменные экземпляра этого класса, указывающие на тот объект, который не установлен в null. Объект автоматически подходит для сбора мусора. Если сохранить объект в GC, используйте этот код...
public class GcTest {
public int id;
public String name;
private static GcTest gcTest=null;
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("In finalize method.");
System.out.println("In finalize :ID :"+this.id);
System.out.println("In finalize :ID :"+this.name);
gcTest=this;
}
public static void main(String[] args) {
GcTest myGcTest=new GcTest();
myGcTest.id=1001;
myGcTest.name="Praveen";
myGcTest=null;
// requesting Garbage Collector to execute.
// internally GC uses Mark and Sweep algorithm to clear heap memory.
// gc() is a native method in RunTime class.
System.gc(); // or Runtime.getRuntime().gc();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("\n------- After called GC () ---------\n");
System.out.println("Id :"+gcTest.id);
System.out.println("Name :"+gcTest.name);
}
}
Выход:
В финализации метода.
В финализации: ID: 1001
В финализации: ID: Praveen
------- После вызова GC() --------
Id: 1001
Имя: Правеен
Интересно, для чего они предназначены - это шаблон с пулами ресурсов (например, для сетевых/db-соединений или потоков), в которых вы используете finalize для возврата ресурса в пул, чтобы фактический объект, содержащий ресурс, t GC'ed.
Глупый пример, в псевдокоде, подобном Java, и отсутствие какой-либо синхронизации:
class SlowResourceInternal {
private final SlowResourcePool parent;
<some instance data>
returnToPool() {
parent.add(this);
}
}
class SlowResourceHolder {
private final SlowResourceInternal impl;
<delegate actual stuff to the internal object>
finalize() {
if (impl != null) impl.returnToPool();
}
}
Я считаю, что для этого есть образец. Не уверен, что это шаблон factory. Но у вас есть один объект, который создает все ваши объекты и содержит ссылку на них. Когда вы закончите с ними, вы отмените ссылку на них в factory, сделав вызов явным.