Подтвердить что ты не робот

Как предотвратить сбор объекта из мусора?

Как предотвратить сбор объекта из мусора?

Есть ли какие-либо подходы путем завершения или ссылки phantom или любых других подходов?

Мне задали этот вопрос в интервью. Интервьюер предположил, что finalize() можно использовать.

4b9b3361

Ответ 1

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

Сборщик мусора собирает только объекты, к которым в вашем приложении нет ссылки. Если нет объекта, который, естественно, ссылался бы на собранный объект, спросите себя, почему он должен оставаться в живых.

Одна пользовательская система, в которой вы обычно не имеете ссылок, но хотите, чтобы объект был одноэлементным. В этом случае вы можете использовать статическую переменную. Одна из возможных реализаций синглтона будет выглядеть так:

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.
  }

Ответ 2

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

Очевидно, что если вы сохраните ссылку на объект в каком-то долгоживущем контексте, он не будет собран, но это не то, о чем спрашивал рекрутер OP. Это не то, что происходит в методе finalize.

Что вы можете сделать, чтобы предотвратить сбор мусора из метода finalize, это написать бесконечный цикл, в котором вы вызываете Thread.yield(); (предположительно, чтобы сохранить пустую петлю от оптимизации):

@Override
protected void finalize() throws Throwable { 
    while (true) { 
        Thread.yield(); 
    } 
} 

Моя ссылка здесь - статья Elliot Back, в которой описывается принудительное утечка памяти этим методом.

Еще один способ, с помощью которого завершать методы - зло..

Ответ 3

Если есть еще ссылка на объект, он не получит сбор мусора. Если ссылок на него нет, вам все равно.

Другими словами - сборщик мусора собирает мусор. Пусть он выполняет свою работу.

Ответ 4

Лучше всего использовать Unsafe, хотя ByteBuffer может быть возможным обходным путем для некоторых случаев.

Также найдите ключевое слово "off-heap" memory.

Опасное

Преимущества по сравнению с ByteBuffer:

  • позволяет объектам быть представленными напрямую, без сериализации и, таким образом, быстрее
  • проверка не ограничена, поэтому быстрее
  • явный контроль освобождения
  • может выделять больше, чем предел JVM

Нелегко работать. Метод описан в следующих статьях:

Все они состоят из следующих шагов:

  • нам нужен оператор sizeof, который у Unsafe не имеет. Как сделать один из них был задан по адресу: В Java, каков наилучший способ определить размер объекта?. Лучшие варианты, скорее всего, это API instrument, но для этого требуется создать Jar и использовать специальные параметры командной строки...

  • когда у нас есть sizeof, выделите достаточно памяти с помощью Unsafe#allocateMemory, которая в основном является malloc и возвращает адрес

  • создать регулярный объект кучи, скопировать его в выделенную память с помощью Unsafe#copyMemory. Для этого вам необходимо указать адрес объекта on-heap и размер объекта

  • установите Object, чтобы указать на выделенную память, затем введите Object в свой класс.

    Невозможно напрямую установить адрес переменной с помощью Unsafe, поэтому нам нужно обернуть объект в массив или объект-оболочку и использовать Unsafe#arrayBaseOffset или Unsafe#objectFieldOffset.

  • как только вы закончите, освободите выделенную память с помощью freeMemory

Если я когда-нибудь получу это, чтобы не выполнить segfault, я отправлю пример: -)

ByteBuffer

Преимущества по сравнению с небезопасными:

  • стабильная версия Java, в то время как Unsafe может сломаться
  • привязывает проверку, поэтому безопаснее, чем... Небезопасно, что позволяет утечки памяти и SIGSEGV

JLS говорит:

Содержимое прямых буферов может находиться вне обычной мусорной корзины.

Пример использования с примитивами:

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;

Связанные темы:

Ответ 5

Я подозреваю, что вы можете иметь в виду, если ваш метод finalize задерживает ссылку на завершаемый объект. В этом случае (если мое чтение Java Language Spec верное), метод finalize никогда не будет повторно запущен, но объект пока не будет собран мусор.

Это не та вещь, которая есть в реальной жизни, кроме, возможно, случайно!

Ответ 6

Это звучит как одно из тех вопросов, которые вы можете задать только по запросу. finalize() запускается, когда ваш объект получает сбор мусора, поэтому было бы довольно извращенным положить что-то там, чтобы предотвратить сбор. Обычно вы просто держите ссылку и все, что вам нужно.

Я даже не уверен, что произойдет, если вы создадите новую ссылку для чего-то в финализаторе - поскольку сборщик мусора уже решил собрать его, вы в итоге получите нулевой ref? В любом случае это плохая идея. например

public class Foo {
   static Foo reference;
  ...
  finalize (){ 
     reference = this; 
  }
}

Я сомневаюсь, что это сработает, или оно может работать, но зависит от имплантации GC или быть "неопределенным поведением". Однако выглядит злым.

Ответ 7

Ключевым моментом является установка реальной ссылочной переменной, указывающей на объект 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
Имя: Правеен

Ответ 8

Интересно, для чего они предназначены - это шаблон с пулами ресурсов (например, для сетевых/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();
    }
}

Ответ 9

Я считаю, что для этого есть образец. Не уверен, что это шаблон factory. Но у вас есть один объект, который создает все ваши объекты и содержит ссылку на них. Когда вы закончите с ними, вы отмените ссылку на них в factory, сделав вызов явным.