Co faktycznie robi flaga JVM CMSClassUnloadingEnabled?

183

Nie mogę przez całe życie znaleźć definicji tego, co CMSClassUnloadingEnabledfaktycznie robi flaga Java VM , poza niektórymi bardzo mglistymi definicjami wysokiego poziomu, takimi jak „pozbywanie się problemów z PermGen” ( czego nie robi , przy okazji).

Zajrzałem na stronę Sun / Oracle i nawet lista opcji tak naprawdę nie mówi, co robi.

Opierając się na nazwie flagi, domyślam się, że CMS Garbage Collector domyślnie nie zwalnia klas, a ta flaga włącza go - ale nie jestem pewien.

Bogaty
źródło

Odpowiedzi:

219

Aktualizacja Ta odpowiedź dotyczy Javy 5-7, Java 8 ma tę poprawkę: https://blogs.oracle.com/poonam/about-g1-garbage-collector,-permanent-generation-and-metaspace Kudos przejdź do mt. uulu

W przypadku języka Java 5-7:

Standardowy wygląd Oracle / Sun VM na świecie to: Klasy są wieczne. Po załadowaniu pozostają w pamięci, nawet jeśli nikogo już to nie obchodzi. Zwykle nie stanowi to problemu, ponieważ nie ma tylu klas czysto „konfiguracyjnych” (= używanych raz do instalacji, a potem nigdy więcej). Więc nawet jeśli zajmą 1 MB, kogo to obchodzi.

Ale ostatnio mamy języki takie jak Groovy, które definiują klasy w czasie wykonywania. Za każdym razem, gdy uruchamiasz skrypt, tworzona jest jedna (lub więcej) nowych klas, które pozostają w PermGen na zawsze. Jeśli korzystasz z serwera, oznacza to, że masz przeciek pamięci.

Jeśli włączysz, CMSClassUnloadingEnabledGC również przeszuka PermGen i usunie klasy, które nie są już używane.

[EDYCJA] Musisz także włączyć UseConcMarkSweepGC(dzięki Samowi Haslerowi ). Zobacz tę odpowiedź: https://stackoverflow.com/a/3720052/2541

Aaron Digulla
źródło
17
Według stackoverflow.com/a/3720052/2541 dla CMSClassUnloadingEnabledmieć żadnego wpływu, UseConcMarkSweepGCmusi być również ustawiony
Sam Haslerem
1
Nie jestem pewien, jak to wpływa na pomysł użycia UseConcatSweepGC, ale wygląda na to, że niedawno naprawiono błąd w CMSClassUnloadingEnabled. Jest to zaznaczone jako naprawione tutaj: bugs.sun.com/bugdatabase/view_bug.do?bug_id=8000325
Bill Rosmus
3
@Kevin: Tak, zdecydowanie. Zobacz na dole groovy.codehaus.org/Running : „Groovy tworzy klasy dynamicznie, ale domyślna VM VM nie obsługuje GG PermGen. Jeśli używasz Java 6 lub nowszej, dodaj -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC. UseConcMarkSweepGCJest konieczne, aby włączyć CMSClassUnloadingEnabled”.
Aaron Digulla,
1
Dobry artykuł na temat używania UseConcMarkSweepGC i CMSClassUnloadingEnabled razem. blog.redfin.com/devblog/2012/06/…
Victor
1
nie jest już ważny w wersji 1.8: blogs.oracle.com/poonam/…
mt.uulu
35

Zgodnie z postem na blogu Najbardziej kompletna lista opcji -XX dla JVM Java , określa, czy rozładowanie klasy jest włączone w module śmieciowym CMS. Domyślnie jest to false. Nie ma innej opcji nazywane ClassUnloadingto jest truedomyślnie które (prawdopodobnie) wpływa na inne śmieciarze.

Chodzi o to, że jeśli GC wykryje, że poprzednio załadowana klasa nie jest już używana w dowolnym miejscu w JVM, może odzyskać pamięć używaną do przechowywania kodu bajtowego klas i / lub kodu natywnego.

Ustawienie CMSClassUnloadingEnabled może pomóc w rozwiązaniu problemu z permgenem, jeśli obecnie używasz modułu zbierającego CMS . Ale są szanse, że nie korzystasz z CMS lub że masz prawdziwy wyciek pamięci związany z modułem ładującym. W tym drugim przypadku klasa nigdy nie będzie wydawać się GC nieużywana ... i dlatego nigdy nie zostanie rozładowana.


Aaron Digulla mówi „zajęcia są na zawsze”. Nie jest to do końca prawdą, nawet w świecie wyłącznie Java. W rzeczywistości czas życia klasy jest powiązany z jej modułem ładującym. Jeśli więc możesz ustalić, że moduł ładujący jest gromadzony w pamięci (i nie zawsze jest to łatwe do zrobienia), klasy, które wczytał, również zostaną usunięte.

W rzeczywistości dzieje się tak, gdy przeprowadzasz ponowne wdrożenie aplikacji internetowej. (A przynajmniej tak powinno się zdarzyć, jeśli można uniknąć problemów, które prowadzą do wycieku permgenów).

Stephen C.
źródło
24

Przykład, w którym jest to przydatne:

Ustawienie -XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabledJVM Weblogic 10.3 pomogło rozwiązać problem polegający na tym, że implementacja JAX-WS tworzyła nową klasę proxy dla każdego wywołania usługi internetowej, co ostatecznie prowadzi do błędów braku pamięci.

Śledzenie nie było trywialne. Poniższy kod zawsze zwracał tę samą klasę proxyport

final MyPortType port = 
Service.create(
        getClass().getResource("/path/to.wsdl"), 
        new QName("http://www.example.com", "MyService"))
    .getPort(
        new QName("http://www.example.com", "MyPortType"), 
        MyPortType.class);

Ten serwer proxy delegował wewnętrznie do instancji weblogic.wsee.jaxws.spi.ClientInstance, która ponownie delegowała się do nowej $Proxy[nnnn]klasy, w której nzwiększano przy każdym połączeniu. Podczas dodawania flag nnadal był zwiększany, ale przynajmniej te klasy tymczasowe zostały usunięte z pamięci.

Mówiąc bardziej ogólnie, może to być bardzo przydatne podczas intensywnego korzystania z odbicie Java i serwerów proxy java.lang.reflect.Proxy

Lukas Eder
źródło
+1 za dzielenie się prawdziwym doświadczeniem. Mieliśmy również ten problem na torquebox, gdzie serwer wygenerował ogromną liczbę klas z powodu procesów kompilacji JRuby.
nurettin
7
zauważ także, że -XX:+CMSPermGenSweepingEnabledjest przestarzałe na korzyść-XX:+CMSClassUnloadingEnabled
nurettin
3
Prawdziwym rozwiązaniem tego problemu jest jednak jednorazowe utworzenie portu i ponowne użycie go. Tak powinien być używany JAX-WS. Port jest również w 100% bezpieczny dla wątków.
rustyx
@rustyx: Czy możesz potwierdzić to roszczenie za pomocą wiarygodnego linku? Zawsze bardzo uważałem na ponowne używanie serwerów proxy i kodów pośredniczących usług sieciowych ... Np. Zobacz wyłączenia odpowiedzialności Apache CXF . Albo to pytanie
Lukas Eder
1
@rukavitsya: Jak powiedziałem w mojej odpowiedzi. Za każdym razem, gdy wywoływałem powyższą logikę, tworzony był nowy serwer proxy
Lukas Eder