GC - zmuszenie do zwolnienia pamięci

0

Szanowni forumowicze,
Chciałbym się dowiedzieć czy można zmusić GC, albo cokolwiek aby usunęło mi instancję obiektu utworzonego operatorem new?
Wywołanie GC po usunięciu referencji na obiekt nie pomaga. GC i tak nie zwalnia pamięci.

Obiekt nigdzie indziej nie ma referencji, więc w tym wypadku jest to bezpieczne, szukałem już dużo, pytałem się ludzi, ale każdy mi pisał, żebym się tym nie zajmował. Tylko chcę zapytać z ciekawości, przy tworzeniu gier w JAVA o chyba musi być jakoś implementowane?
Zauważyłem, że obiekty w JAVA jakoś szybko poza Eden. Jak jakiś obiekt zajmuje kilkadziesiąt MB to może być czsami problem i chyba jest bo aplikacje w JAVA są raczej pamięciożerne.

1

Generalnie to nawet delete w C++ nie musi od razu zwalniać pamięci. Powinieneś po prostu zastosować się do poradników dotyczących alokacji pamięci z generacyjnymi odśmiecaczami - najlepiej, aby tworzone obiekty albo były natychmiastowo osieracane (tzn zwalniane referencje do nich) albo były długowieczne.

Wywołanie System.gc() to tylko podpowiedź dla odśmiecacza, którą może całkowicie zignorować.

0

Ale nie ma nic natywnego w JAVA do usuwania obiektu z pamięci? Nic, tak w ogóle?

W C# obiekty miały dispose(), pomimo tego, że jest GC.
Jak sobie pisałem gierkę w C# to mi to ułatwiało wiele spraw. Tutaj musiałbym za każdym razem np. usuwania pocisku wywalić go tylko z listy rysowanych elementów (no i kolizji itp.) i czekać aż łaskawie Szanowny Pan GC zechce sobie to usunąć, a jak obiekt trafi poza eden to dupa blada. A jak się ma kod rozwijany systematycznie np. przez rok, to się nie chce szukać wszytkich referencji. Bo zauważyłem, że jak mam 2 obiekty, do których są usunięte referencje, ale one mają do siebie referencje to GC nie usunie ich....
W C# robiłem sobie z pociskiem dispose() i grało:-) Żadnego zamiatania pod dywan. A jak pocisków są miliony? I potem miliony odłamków z tych milionów?

2

Dispose usuwa obiekt? http://4programmers.net/Forum/C_i_.NET/78245-Co_wlasciwie_robi_metoda_dispose

Wątpię, aby była jakakolwiek metoda do usuwania obiektu zarządzanego z pamięci. Referencje, czy to w C# czy Javie, albo są nullami albo prowadzą do żyjącego obiektu (być może z wywołanym na nim dispose() czy finalize() ale to już szczegół). Nie ma możliwości takiego usunięcia obiektu zarządzanego, aby pozostały po nim uszkodzone referencje.

Czemu jak obiekt trafi poza Eden to dupa blada? Odśmiecanie odśmiecaniu nierówne, w większości przejść analizowany jest tylko Eden, ale co jakiś czas wywracana jest cała sterta do góry nogami.

1
arrowman napisał(a)

Ale nie ma nic natywnego w JAVA do usuwania obiektu z pamięci? Nic, tak w ogóle?

W Javie w ogóle prawie nic nie ma natywnego. :)

Jak sobie pisałem gierkę w C# to mi to ułatwiało wiele spraw. Tutaj musiałbym za każdym razem np. usuwania pocisku wywalić go tylko z listy rysowanych elementów (no i kolizji itp.) i czekać aż łaskawie Szanowny Pan GC zechce sobie to usunąć

Przydział pamięci jest poza operacją na dysku najwolniejszą operacją jaką wykonuje JVM. Należy każdy new traktować pod względem wydajności podobnie jak czytanie z pliku - to znacznie ułatwi projektowanie czegokolwiek. Poza tym pozostawianie "niepotrzebnych" referencji, albo jest celowe, albo jest skutkiem błędu. Typowym przykładem jest zdejmowanie ze stosu referencji i nie wyczyszczenie ze stosu miejsca po niej - jednak czasem może to być celowe pozostawienie jako specyficzny rodzaj cache.

Bo zauważyłem, że jak mam 2 obiekty, do których są usunięte referencje, ale one mają do siebie referencje to GC nie usunie ich...

Bo usunie dopiero wtedy kiedy będzie brakowało wolnej pamięci w taki sposób, że JVM będzie musiało rozszerzyć swoją pamięć, przez pobranie kolejnej porcji z systemu. Wtedy dopiero obiekty będą masowo zwalniane, a wolne miejsce defragmentowane. To właśnie ta ostatnia operacja powoduje, że odśmiecanie jest bardziej efektywne, kiedy na raz da się zwolnić dużo. Jeżeli po każdej utracie referencji śmieciarz miałby odzyskiwać tę pamięć, to prowadziłoby to do takiej fragmentacji RAMu JVM, że w pewnym momencie i tak trzeba by było przeprowadzić bardzo kosztowną czasowo defragmentację. Efektywniej więc robić ją wtedy kiedy jest dużo obiektów do zwolnienia na raz.
Objawia się to tak, że zmniejsza się nieustannie "wolna" ilość pamięci i kiedy dojdzie ona do zera, to nagle odzyskuje się kilka do kilkudziesięciu MB na raz, następnie proces powtarza się. Oczywiście w sytuacji kiedy średnie użycie pamięci wciąż jest stałe. Kiedy rośnie, to JVM podwyższa pamięć przez zabranie jej systemowi i cały proces powtarza się na wyższym poziomie zużycia.

W C# robiłem sobie z pociskiem dispose() i grało:-) Żadnego zamiatania pod dywan. A jak pocisków są miliony? I potem miliony odłamków z tych milionów?

Polecenie new dlatego jest dość wolne, że właśnie kiedy śmieciarz odkryje dużą potrzebę przydziałów, a zupełnie nieużywanej pamięci jest za mało, to zaczyna zajmować się odśmiecaniem. Po prostu nie przejmuj się w ogóle nieużywanymi zasobami. Profiler pokaże, że jest to problem tylko jeżeli spore spowolnienia będą wyskakiwać cały czas w zupełnie losowych miejscach kodu. Ale jest to mało prawdopodobne.

2

Przydział pamięci jest poza operacją na dysku najwolniejszą operacją jaką wykonuje JVM. Należy każdy new traktować pod względem wydajności podobnie jak czytanie z pliku - to znacznie ułatwi projektowanie czegokolwiek.

A skąd takie rewelacje?
http://shootout.alioth.debian.org/u64q/benchmark.php?test=binarytrees&lang=all
Zwykła alokacja + dealokacja jest szybsza w Javie niż w C/ C++. Pule obiektów w C/ C++ są szybsze w tym benchmarku, ale to chyba logiczne.

1
Wibowit napisał(a)

A skąd takie rewelacje?

Z własnego doświadczenia. Operacje alokacji i dealokacji są w Javie szybkie, to fakt. Ale nie szybsze niż wykonywanie pojedynczych instrukcji z jakich składa się kod (umownie źródłowy) Javy. Choćby z tego powodu, że alokacja lub odśmiecanie mogą wywoływać wykonanie całych serii takich instrukcji (nawet jeżeli będą one natywne/maszynowe). Kolega chce użyć Javy do gier. Dlatego bezpiecznie założyć, że dopóki nie ma operacji przydziału pamięci, to kod jest maksymalnie szybki. A operacje, które takie alokacje zawierają traktować jako potencjalnie wolniejsze od tych, które alokacji/dealokacji nie zawierają w ogóle.

0

A próbowałeś użyć System.gc(); ?

 
Runs the garbage collector.

Calling the gc method suggests that the Java Virtual Machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse. When control returns from the method call, the Java Virtual Machine has made a best effort to reclaim space from all discarded objects.

The call System.gc() is effectively equivalent to the call:

     Runtime.getRuntime().gc()
     

See Also:
    Runtime.gc()

http://download.oracle.com/javase/7/docs/api/java/lang/System.html#gc%28%29

0

Rzuć okiem do profilera i debugera. Możliwe, że zwolniłeś tylko jedną z referencji, ale wiszą jeszcze jakieś niejawne odwołania. Teoretycznie GC powinien umieć wykryć cykl osieroconych obiektów, ale nie zawsze to działa.

1 użytkowników online, w tym zalogowanych: 0, gości: 1