2014-06-11 10 views
9

Wczoraj wdrożyłem moją pierwszą aplikację Grails (2.3.6) na serwer dev i zacząłem ją monitorować. Właśnie dostałem automatyczny monitor stwierdzający, że procesor został przypięty do tego komputera, więc go włączyłem. Uruchomiłem top i odkryłem, że to PID mojej aplikacji Java przypinał serwer. Zauważyłem też, że pamięć wynosi 40%. Po kilku sekundach procesor przestał przypinać, spadł do normalnego poziomu, a pamięć wróciła do zakresu ~ 20%. Klasyczny główny GC.Zrozumienie wycieku klasy Groovy/Grails

Podczas zbierania zrobiłem kupę sterty. Po GC otworzyłem zrzut w JVisualVM i zobaczyłem, że większość pamięci była przydzielana dla klasy org.codehaus.groovy.runtime.metaclass.MetaMethodIndex.Entry. Łącznie było ich ponad 250 000, pobierając około 25 MB pamięci.

I googled tej klasy i spojrzał na to ultra helpful Javadocs. Wciąż nie mam pojęcia, co robi ta klasa.

Jednak w googlowaniu pojawiły się także około tuzin artykułów pokrewnych (niektóre z pytań SO) dotyczących tej klasy i przeciek PermGen/classloader z aplikacjami Grails/Groovy. I choć wygląda na to, że moja aplikacja rzeczywiście wyczyściła te 250K wystąpienia z GC, nadal niepokoi, że było tak wiele jego instancji, a GC przypięła procesor przez ponad 5 minut.

Moje pytania:

  • Co to jest klasa i jaka jest Groovy robi z nim?
  • Czy ktoś może mi wyjaśnić this answer? Dlaczego -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled pomógłby rozwiązać ten konkretny problem?
  • Dlaczego ta klasa jest szczególnie kłopotliwa dla PermGen?

Odpowiedz

12

Groovy jest językiem dynamicznym, każde wywołanie metody jest wywoływane dynamicznie. Aby zoptymalizować, że Groovy tworzy MetaClass dla każdego java.lang.Class w MetaClassRegistry. Te instancje MetaClass są tworzone na żądanie i przechowywane przy użyciu słabych odwołań.

Powodem, dla którego widzisz dużo org.codehaus.groovy.runtime.metaclass.MetaMethodIndex.Entry jest to, że Groovy zapisuje w pamięci mapę map i metod, aby mogły być szybko wysłane przez środowisko wykonawcze. W zależności od wielkości aplikacji może to być tak, jakbyś odkrył tysiące klas, ponieważ każda klasa może mieć dziesiątki czasami setki metod.

Jednak nie ma "wycieku pamięci" w Groovy i Grails, to, co widzisz, jest normalnym zachowaniem. W aplikacji kończy się pamięć, prawdopodobnie dlatego, że nie przydzielono jej wystarczającej ilości pamięci, co z kolei powoduje, że instancje są usuwane z pamięci. Teraz mówią na przykład masz pętlę:

for(str in strings) { 
    println str.toUpperCase() 
} 

W tym przypadku mamy do wywołującego metodę na klasy String. Jeśli masz mało pamięci, to co się stanie, to, że dla każdej iteracji pętli MetaClass będzie zbierane śmieci, a następnie ponownie tworzone dla następnej iteracji. Może to dramatycznie spowolnić działanie aplikacji i doprowadzić do przypięcia procesora tak, jak widzieliście. Ten stan jest powszechnie określany jako "odejście z metaklasu" i jest znakiem, że aplikacja ma za mało pamięci sterty.

Jeśli Groovy nie było śmieci zbierania tych metaklasa instancje następnie tak oznaczałoby to jest przeciek pamięci w Groovy, ale fakt, że jest śmieci zbieranie tych klas jest to znak, że wszystko jest dobrze, z wyjątkiem fakt, że nie przydzielono wystarczającej ilości pamięci sterty. Nie oznacza to, że może wystąpić wyciek pamięci w innej części aplikacji, która pochłania całą dostępną pamięć i nie pozostawia zbyt wiele, aby Groovy działał poprawnie.

Jeśli chodzi o inne odpowiedzi, do których się odnosisz, dodanie rozładunku klas i ulepszeń PermGen nie spowoduje żadnych problemów z pamięcią, chyba żebędziesz dynamicznie analizować klasy w czasie wykonywania. Przestrzeń PermGen jest używana przez maszynę JVM do przechowywania dynamicznie tworzonych klas. Groovy umożliwia kompilowanie klas w czasie wykonywania przy użyciu GroovyClassLoader.parseClass lub GroovyShell.evaluate. Jeśli ciągle analizujesz klasy, to może pomóc dodanie flag rozładowujących klasę. Zobacz też ten wpis:

Locating code that is filling PermGen with dead Groovy code

jednak typowa aplikacja Grails nie ma dynamicznie kompilować klas w czasie wykonywania, a więc szczypanie PermGen i ustawienia klasa rozładunku nie będą faktycznie coś osiągnąć.

Należy sprawdzić, czy przydzielono wystarczającą ilość pamięci sterty za pomocą opcji -Xmx i jeśli nie można przydzielić więcej.