Jak zapobiec zbieraniu śmieci przez obiekt?

Jak zapobiec zbieraniu śmieci przez obiekt?

Czy są jakieś podejścia przez finalize, phantom reference lub inne podejścia?

Zadano mi to pytanie w wywiadzie. Ankieter zasugerował, że można użyć finalize().
Author: Rahul Garg, 2009-08-25

10 answers

/ Align = "left" / Jeśli twój obiekt zostanie zebrany przedwcześnie, jest to objaw, że masz błąd w projektowaniu aplikacji.

Garbage collector zbiera tylko obiekty, do których nie ma odniesienia w aplikacji. Jeśli nie ma obiektu, który naturalnie odwołałby się do zebranego obiektu, zadaj sobie pytanie, dlaczego powinien być utrzymywany przy życiu.

Jedną z zastosowań, w której zazwyczaj nie masz referencji, ale chcesz zachować obiekt, jest singleton. W tym przypadku, ty przydałaby się zmienna statyczna. Jedna z możliwych implementacji Singletona wyglądałaby tak:

public class Singleton {
  private static Singleton uniqueInstance;

  private Singleton() {
    }

  public static synchronized Singleton getInstance() {
    if (uniqueInstance == null) {
      uniqueInstance = new Singleton();
    }
    return uniqInstance;
  }
}

Edit: technicznie rzecz biorąc, możesz zapisać odniesienie gdzieś w finalizatorze. Uniemożliwi to zbieranie obiektu, dopóki kolektor nie stwierdzi, że nie ma więcej odniesień. Finalizer zostanie wywołany najwyżej raz, więc musisz upewnić się, że Twój obiekt (łącznie z jego superklasami) nie musi być finalizowany po pierwszej kolekcji. Ja bym radzę jednak nie używać tej techniki w rzeczywistych programach. (to zostawi takich kolegów jak ja krzyczących WTF!? ;)

  protected void finalize() throws Throwable {
    MyObjectStore.getInstance().store(this);
    super.finalize(); // questionable, but you should ensure calling it somewhere.
  }
 32
Author: Tobias,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-25 18:33:13

Sztuczka, której szukał twój rozmówca, polega prawdopodobnie na tym, że chce, abyś wiedział, że możesz zapobiec usuwaniu śmieci z obiektu, wymuszając wyciek pamięci.

Oczywiście, jeśli zachowasz odniesienie do obiektu w jakimś długotrwałym kontekście, nie zostanie ono zebrane, ale nie o to pytał rekruter OP. To nie jest coś, co dzieje się w metodzie finalize.

Co możesz zrobić, aby zapobiec usuwaniu śmieci z metody finalize jest to zapis nieskończonej pętli, w której wywołujesz Thread.yield(); (prawdopodobnie, aby zapobiec optymalizacji pustej pętli):

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

Moim odniesieniem jest Artykuł Elliot Back , w którym opisano wymuszanie wycieku pamięci za pomocą tej metody.

Kolejny sposób, w jaki finalizacja metod jest zła.

 9
Author: CPerkins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-26 12:59:44

Najlepszym sposobem jest użycie niebezpiecznego , chociaż ByteBuffer może być możliwym obejściem w niektórych przypadkach.

Szukaj również słowa kluczowego pamięć "off-heap".

Niebezpieczne

Zalety nad ByteBuffer:

    Pozwala na bezpośrednie reprezentowanie obiektów, bez serializacji, a tym samym szybsze.]}
  • no bounds checking, so faster
  • explicit deallocation control
  • może przydzielić więcej niż JVM limit
Nie jest jednak łatwo dostać się do pracy. Metoda jest opisana w następujących artykułach:

Wszystkie składają się z następujących kroków:

  • Potrzebujemy sizeof operator, którego nie ma. Jak zrobić jeden został zapytany w: w Javie, jaki jest najlepszy sposób, aby określić rozmiar obiektu?. Najlepszą opcją jest prawdopodobnie API instrument, ale wymaga to utworzenia Jar i użycia specjalnych opcji wiersza poleceń...

  • Gdy mamy sizeof, przydzielamy wystarczającą ilość pamięci za pomocą Unsafe#allocateMemory, która jest w zasadzie malloc i zwraca adres

  • Utwórz zwykły obiekt On heap, skopiuj go do przydzielonej pamięci za pomocą Unsafe#copyMemory. Aby to zrobić, musisz podać adres obiektu on-heap i rozmiar obiektu

  • Ustaw Object, aby wskazywała przydzieloną pamięć, a następnie wrzuć Object do swojej klasy.

    Nie wydaje się możliwe ustawienie adresu zmiennej bezpośrednio z Unsafe, więc musimy zawinąć obiekt w tablicę lub obiekt wrapper i użyć Unsafe#arrayBaseOffset lub Unsafe#objectFieldOffset.

  • Po zakończeniu zwolnij przydzieloną pamięć za pomocą freeMemory

If I ever get this to not segfault I will post an example: -)

ByteBuffer

Zalety nad bezpieczeństwem:

  • stabilne w wersjach Java, podczas gdy niebezpieczne mogą się zepsuć
  • sprawdza, więc bezpieczniej niż... Unsafe, który pozwala na wycieki pamięci i SIGSEGV

JLS says :

Zawartość buforów bezpośrednich może znajdować się poza normalną stertą śmieci.

Przykład użycia z prymitywni:

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;

Powiązane wątki:

 4
Author: Ciro Santilli 709大抓捕 六四事件 法轮功,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:30

Jeśli nadal istnieje odniesienie do obiektu, nie zostanie pobrany śmieci. Jeśli nie ma żadnych odniesień do niego, nie powinno cię to obchodzić.

Innymi słowy-garbage collector zbiera tylko śmieci. Niech robi swoje.

 3
Author: Thomas Owens,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-25 18:02:53

Podejrzewam, że możesz odnosić się do tego, czy Twoja metoda finalize ukryje odniesienie do finalizowanego obiektu. W tym przypadku (jeśli mój odczyt specyfikacji języka Java jest poprawny) metoda finalize nigdy nie zostanie ponownie uruchomiona, ale obiekt nie zostanie jeszcze pobrany.

To nie jest coś, co robi się w prawdziwym życiu, chyba że przez przypadek!

 2
Author: Simon Nickerson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-25 18:19:22

To brzmi jak jedno z tych pytań, które zobaczysz tylko w czasie rozmowy. funkcja finalize() jest uruchamiana, gdy twój obiekt zbiera śmieci, więc byłoby dość przewrotne umieścić tam coś, aby zapobiec zbieraniu. Normalnie po prostu trzymasz referencję i to wszystko, czego potrzebujesz.

Nie jestem nawet pewien, co by się stało, gdybyś stworzył nowe odniesienie do czegoś w finalizatorze - skoro garbage collector już zdecydował się je zebrać, czy wtedy skończyłbyś z NULL ref? W każdym razie to kiepski pomysł. np.

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

Wątpię, czy to zadziała, czy może działać, ale zależy od implenetacji GC, czy może być "nieokreślonym zachowaniem". Ale wygląda źle.

 2
Author: Steve B.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-25 18:19:26

Kluczowym punktem jest ustawienie rzeczywistej zmiennej referencyjnej wskazującej na obiekt null, chociaż mamy zmienne instancji tej klasy wskazujące na ten obiekt, który nie jest ustawiony na null. Obiekt automatycznie kwalifikuje się do usunięcia śmieci.jeśli Zapisz obiekt do GC, użyj tego kodu...

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);


    }

}

Wyjście:

W metodzie finalize.
In finalize: ID: 1001
In finalize: ID: Praveen

------- Po nazwie GC () --------

Id: 1001
Nazwa : Praveen

 1
Author: Praveen Kumar Verma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-11 20:14:20

Zastanawiam się, czy chodzi o wzorzec z pulami zasobów (np. dla połączeń sieciowych/db lub wątków), w którym używasz finalize, aby zwrócić zasób do puli, tak aby rzeczywisty obiekt przechowujący zasób nie był GC ' ed.

Głupi przykład, w pseudokodzie Javy i brak jakiejkolwiek synchronizacji:

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();
    }
}
 1
Author: Ed Handy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-18 23:34:47

Wierzę, że istnieje wzór na to. Nie jestem pewien, czy to wzór fabryczny. Ale masz jeden obiekt, który tworzy wszystkie obiekty i zawiera odniesienie do nich. Kiedy skończysz z nimi, odwoływasz się do nich w fabryce, czyniąc połączenie wyraźnym.

 0
Author: Zoidberg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-25 18:08:03

Mamy trzy sposoby osiągnięcia tego samego - 1) zwiększenie wielkości przestrzeni sterty-Eden . 2) Utwórz klasę Singleton ze statycznym odniesieniem . 3) Override finalize () metoda i nigdy nie pozwól, aby ten obiekt dereferencji.

 0
Author: Satyendra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-30 16:00:01