1. jmap: magia wiersza poleceń
Automatyczny odśmiecacz (garbage collector) — to oczywiście świetna sprawa. Ale nawet aplikacje Java mogą „przeciekać” (OutOfMemoryError), działać wolno lub nagle zwalniać z powodu niewłaściwego użycia pamięci. Przyczyny mogą być różne:
- Wyciek pamięci (memory leaks): obiekty, które powinny zostać usunięte, nadal „żyją”.
- Zbyt duże ilości danych w kolekcjach.
- Błędy w logice cache’owania.
- Niewyzwalniane zasoby (np. strumienie, pliki).
Twoje zadanie — nauczyć się szybko znajdować takie problemy. W przeciwnym razie ryzykujesz, że zostaniesz nie programistą, a „twórcą bugów”!
Czym jest jmap?
jmap to narzędzie wchodzące w skład standardowego pakietu JDK. Pozwala uzyskać „zrzut” (heap dump) pamięci działającego procesu Javy. Taki zrzut można potem otworzyć w innych narzędziach i sprawdzić, jakie obiekty zajmują pamięć, ile ich jest i kto się do nich odwołuje.
Heap dump to jak zdjęcie twojej sterty: wszystkie obiekty, które się w niej znajdują, ich typy, powiązania i rozmiary.
Jak używać jmap
Znajdź PID procesu
Na początek trzeba ustalić identyfikator procesu (PID), w którym działa twoja aplikacja Java. Można to zrobić na kilka sposobów:
Przez jps (kolejne narzędzie z JDK):
jps -l
Zobaczysz listę procesów Javy i ich PID.
Przez Menedżera zadań (Task Manager) lub ps w Linuksie.
Zrób zrzut pamięci
jmap -dump:format=b,file=heap.bin <PID>
- format=b — format binarny (odpowiedni do analizy).
- file=heap.bin — nazwa pliku, do którego zostanie zapisany zrzut.
- <PID> — identyfikator procesu.
Przykład:
jmap -dump:format=b,file=heap.bin 12345
Co zrobić z heap dumpem?
Zrzut zazwyczaj analizuje się w narzędziach graficznych (np. jvisualvm lub Eclipse MAT), bo ręczne szukanie obiektów w pliku binarnym to już wyższa szkoła jazdy i odrobina masochizmu.
Inne możliwości jmap
Podgląd statystyk pamięci:
jmap -heap <PID>
Pokazuje informacje o stercie: rozmiar, używany GC, stan.
Lista klas:
jmap -histo <PID>
Pokazuje statystyki według klas: ile obiektów danego typu i ich łączny rozmiar.
Przykładowy wynik:
| # | Obiekt | Liczba | Bajty |
|---|---|---|---|
| 1 | |
1200 | 48000 |
| 2 | |
1000 | 32000 |
| 3 | |
200 | 12800 |
To już daje obraz: jeśli nagle masz miliony obiektów jakiegoś typu — jest powód do zastanowienia.
2. jvisualvm: wizualny analizator pamięci
jvisualvm to program graficzny wchodzący w skład JDK (zobacz w katalogu bin). Pozwala podłączać się do lokalnych (a czasem i zdalnych) procesów Javy, monitorować je w czasie rzeczywistym, wykonywać heap dump, oglądać statystyki pamięci, wątków, GC, CPU, a nawet profilować wykonanie kodu.
Jeśli lubisz klikać myszką i oglądać ładne wykresy — to narzędzie jest dla ciebie.
Jak uruchomić jvisualvm
W wierszu poleceń (albo przez skrót w Windows):
jvisualvm
Otworzy się okno, w którym zobaczysz listę wszystkich lokalnych procesów Javy.
Główne funkcje jvisualvm
Monitorowanie pamięci w czasie rzeczywistym
- Wybierz proces na liście po lewej.
- Przejdź na kartę Monitor.
- Zobaczysz wykresy użycia sterty, CPU, liczbę wątków i klas.
Przykład:

Heap Dump (zrzut pamięci)
- Na panelu Monitor kliknij Heap Dump.
- Po kilku sekundach pojawi się karta z analizą zrzutu.
- Zobaczysz listę wszystkich klas, liczbę obiektów oraz ich łączny rozmiar.
Wyszukiwanie „ciężkich” obiektów
- Sortuj według rozmiaru lub liczby.
- Jeśli widzisz, że jakiś typ (np. ArrayList lub String) zajmuje zbyt dużo pamięci — to powód do dochodzenia.
Analiza referencji (Reference Graph)
- Kliknij klasę — zobaczysz, kto odwołuje się do obiektów tej klasy.
- Można dojść do źródła: dlaczego obiekt nie jest usuwany przez garbage collector.
Analiza wątków
- Karta Threads — pokazuje wszystkie wątki i ich stan.
- Można wykryć „zawieszone” lub zbyt aktywne wątki.
Profilowanie
- Karta Profiler — pozwala mierzyć, które metody zajmują najwięcej czasu lub pamięci.
- To już głęboka analiza, ale do szukania wycieków pamięci wystarczą pierwsze kroki.
3. Praktyka: analizujemy wyciek pamięci
Przykład kodu z wyciekiem
import java.util.ArrayList;
import java.util.List;
public class MemoryLeakDemo {
// Statyczna kolekcja — klasyka gatunku!
private static final List<String> bigList = new ArrayList<>();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1_000_000; i++) {
bigList.add("Wiersz numer " + i);
if (i % 100_000 == 0) {
System.out.println("Dodano: " + i);
Thread.sleep(500); // Dajemy czas na analizę
}
}
System.out.println("Gotowe! Nie zamykaj aplikacji, otwórz jvisualvm.");
Thread.sleep(600_000); // 10 minut — czas na analizę
}
}
Co się dzieje?
- Tworzymy statyczną listę i dodajemy do niej milion napisów.
- Po zakończeniu dodawania program „zawisa” (czeka), abyś mógł podłączyć się do niego narzędziami analizy.
Analiza przez jvisualvm
- Uruchom program.
- Otwórz jvisualvm i wybierz proces.
- Na karcie Monitor obejrzyj wykres pamięci.
- Jeśli wszystko jest w porządku, pamięć po GC powinna się „zwalniać”.
- Jeśli jest wyciek, pamięć tylko rośnie.
- Wykonaj Heap Dump.
- Sprawdź, które obiekty zajmują najwięcej pamięci.
- W naszym przypadku — java.util.ArrayList i java.lang.String.
- Kliknij obiekt — zobacz, kto się do niego odwołuje.
- Zobaczysz, że to statyczne pole bigList.
Jak to naprawić?
- Usuwaj zbędne elementy z kolekcji, ograniczaj ich wzrost.
- Zeruj odwołania do „ciężkich” obiektów, gdy nie są już potrzebne.
- Nie trzymaj dużych kolekcji w static, jeśli nie są potrzebne cały czas.
4. Inne narzędzia: Eclipse MAT, jconsole
Eclipse Memory Analyzer (MAT)
Eclipse MAT to bezpłatne, ale bardzo mocne narzędzie do analizy heap dump. Pozwala zobaczyć, jakie obiekty zajmują pamięć i które z nich utrzymują inne — tak zwany retained set. Dzięki temu można precyzyjnie ustalić, gdzie kryje się wyciek.
Narzędzie potrafi budować szczegółowe raporty o podejrzanych obiektach (leak suspects) i spokojnie radzi sobie nawet z gigantycznymi zrzutami o rozmiarze kilku gigabajtów.
Działa to prosto: wykonujesz zrzut pamięci za pomocą jmap lub jvisualvm, otwierasz go w MAT, klikasz przycisk Leak Suspects Report — i dostajesz gotowy raport z już podświetlonymi potencjalnymi problemami.
jconsole
jconsole to proste i wygodne narzędzie do obserwowania JVM w czasie rzeczywistym. Pokazuje, ile pamięci jest używane, jak obciążony jest procesor, ile wątków pracuje i jak zachowuje się garbage collector.
W odróżnieniu od bardziej zaawansowanych narzędzi, takich jak VisualVM, jconsole nie analizuje heap dump, ale świetnie nadaje się do szybkiej diagnostyki — kiedy trzeba jednym rzutem oka zrozumieć, co dzieje się wewnątrz aplikacji.
5. Typowe błędy przy analizie pamięci
Błąd nr 1: Zrzucasz pamięć nie tego procesu. Często na maszynie działa wiele aplikacji Java i można pomylić PID. Sprawdź przez jps i upewnij się, że analizujesz właśnie swój program.
Błąd nr 2: Oczekujesz „wycieku” tam, gdzie go nie ma. GC może nie usuwać obiektów od razu — czasem pamięć „zwalnia się” dopiero po Full GC. Nie panikuj, jeśli po jednej iteracji pamięć się nie zwolniła — spójrz na trend.
Błąd nr 3: Nie uwzględniasz wpływu obiektów statycznych/cache’owanych. Wiele wycieków wynika z tego, że obiekty są przechowywane w polach static lub globalnych kolekcjach. Sprawdzaj, kto odwołuje się do „ciężkich” obiektów — to często static.
Błąd nr 4: Nie używasz profilowania w czasie działania. Heap dump to dobrze, ale czasem ważne jest obserwować wzrost pamięci w czasie (wykresy w jvisualvm). Jeśli pamięć stabilnie rośnie — jest powód do dochodzenia.
Błąd nr 5: Nie usuwasz nasłuchiwaczy/obsługi zdarzeń. Jeśli zasubskrybowałeś zdarzenie, ale się nie wypisałeś — obiekt będzie „żył” w pamięci, nawet jeśli już go nie używasz.
Wskazówka: Nie bój się eksperymentować z narzędziami! Rób zrzuty, oglądaj wykresy, klikaj obiekty — tylko tak nauczysz się „czuć” pamięć swojej aplikacji i szybko znajdować problemy.
GO TO FULL VERSION