Wycieki pamięci nie muszą być trudną zagadką. Gdy pamięć ucieka i nie wiadomo tak naprawdę czym zawiniliśmy warto użyć, chociażby takiego narzędzia, jak Java VisualVM.
Aby odnaleźć wycieki pamięci w naszych programach, należy zaopatrzyć się w pewne proste narzędzie Java VisualVM do pobrania z tej strony https://visualvm.github.io/.
Dodatkowo można pobrać gotowy już program wywołujący wycieki pamięci oraz jego kod źródłowy.
Uruchamiamy najpierw program Java VisualVM
Teraz możemy uruchomić program wywołujący wyciek pamięci. Można pobrać i uruchomić Memory Leak Desktop Tester lub jakąś własną z tego typu problemem aplikację. Ja posłużę się wyżej wymienioną.
Teraz możemy zauważyć, że nasz program jest widoczny w programie Java VisualVM. Należy go wybrać z listy i kliknąć dwukrotnie. W pierwszej zakładce znajdziemy kilka ogólnych informacji o monitorowanej aplikacji.
W kolejnej zakładce Monitor można oglądać bieżący wpływ aplikacji na środowisko wirtualnej maszyny Javy (JVM), wykorzystanie procesora, pamięci, liczbę używanych klas, oraz liczbę wątków. Z tego miejsca może również wywołać Garbage Collector klikając Perform GC, jak i zrobić zrzut pamięci klikając Heap Dump.
Następna zakładka pozwala na dokładniejsze monitorowanie działania wątków w aplikacji. Oprócz podstawowych informacji o ilości wątków widziana jest bieżąca lista poszczególnych wątków działających w naszej aplikacji oraz ich stan w skali czasu jak ich czas działania. W tym miejscu możemy również zrobić zrzut tych danych klikając Thrad Damp. Monitorowanie przebiegu wykonania wątków daje nam możliwość również wykrycie zakleszczeń.
Kolejną zakładka Sampler pozwala na testowanie działania aplikacji pod względem czasu wykonywania metod, ilości wykonanych metod, pozwala sprawdzić, w jakim stopniu korzystają one z czasu procesora.
Oprócz tego możliwe jest również monitorowanie wątków pod względem wykorzystania czasu procesora.
Sampler pozwala również na proste badanie pamięci pod względem liczby instancji oraz ogólnym rozmiarze wszystkich obiektów z danego typu. Dodatkowo umożliwia również monitorowanie pamięć pod względem wykorzystywania jej przez działające wątki.
Najbardziej interesująca nas zakładką jest Profiler. To tu odpowiednio konfigurując narzędzie, jesteśmy w stanie dokładnie wyśledzić metodę, która jest powodem wycieku pamięci. Zaznaczamy Settings wybieramy na dole ekranu zakładkę Memory Settings. Następnie zaznaczamy Record allocations stack traces.
Teraz klikamy Memory, a następnie wywołujemy funkcjonalność powodującą wyciek pamięci w naszym programie. W moim przypadku będzie to kliknięcie Start w programie Memory Leak Tester.
Czkamy aż profiler zbierze odpowiednią ilość danych do sprawdzania naszego wycieku. W przypadku mojego programu jest to czas około minuty. Następnie klikamy na Snapshot aby zapisać zebrane podczas analizy informacje o działaniu programu.
Gdy wykonamy już Snapshot możemy przejść do niego i sprawdzić zarwate w nim informacje. Segregujemy po kolumnie Live Bytes, czyli obiektach, które zajęły najwięcej pamięci w trakcie działania programu. W moim przypadku są to obiekty typu byte[] i ConcurentLinkedQueue$Node. Zaznaczam więc na początek byte[] i klikam prawym Show Allocation Stack Traces. Następnie ukazuje mi się poniższy ekran.
Tutaj znów sortujemy dane po kolumnie Live Bytes i od razu można zauważyć, że w drugim wierszu jest wyświetlony pakiet oraz klasa w nim zawarta wraz ze szkodliwą metoda wytwarzającą nadmierną ilość obiektów typy byte[], która powoduje wycieki pamięci. Dzięki temu będziemy mogli łatwo odnaleźć odpowiedzialny za to kod w naszym programie.
Sprawdźmy jeszcze, skąd się biorą obiekty drugiej wspomnianej klasy, czyli ConcurentLinkedQueue$Node. Klikamy na poniższej zakładce Memory Results i sprawdzamy w ten sam sposób skąd nagromadzenie tych drugich obiektów. Jak widać na poniższym obrazku, winna jest ta sama metoda.
No i się znalazła się nasza winowajczyni. Teraz już wiemy w jakim pakiecie, klasie i jakiej metodzie tkwi problem z naszym wyciekiem pamięci.