0Pricing
Java Academy · Lekcja

Wykrywanie i naprawianie wycieków pamięci

Identyfikuj typowe wzorce wycieków pamięci (kolekcje statyczne, listenery, pamięci podręczne) i naprawiaj je.

Wykrywanie i naprawianie wycieków pamięci to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.

Czym jest wyciek pamięci w Javie?

Wyciek pamięci w Javie występuje, gdy obiekty nie są już potrzebne, ale pozostają silnie osiągalne, uniemożliwiając GC odzyskanie zajmowanej przez nie pamięci. Sterta rośnie, aż zostanie zgłoszony wyjątek OutOfMemoryError.

Nagromadzenie w kolekcji statycznej

Pole statyczne przechowujące stale rosnącą kolekcję jest klasycznym przykładem wycieku. Dodane obiekty, których nigdy nie usunięto, pozostają aktywne przez cały czas działania aplikacji.

public class Cache {
    // LEAK: static list grows forever if items are never removed
    private static final List<Object> items = new ArrayList<>();
    public static void add(Object o) { items.add(o); }
    // Fix: add a remove() method or use a bounded cache
}

Niewyrejestrowane listenery zdarzeń

Zarejestrowanie listenera bez jego późniejszego usunięcia utrzymuje przy życiu zarówno listener, jak i wszystkie obiekty, do których się odwołuje. Należy zawsze wyrejestrowywać listenery, gdy nie są już potrzebne.

// Leak:
sensor.addListener(new DataLogger());
// Fix:
DataLogger logger = new DataLogger();
sensor.addListener(logger);
// ... when done:
sensor.removeListener(logger);

Nieoczyszczone zmienne ThreadLocal

W pulach wątków wartości ThreadLocal przetrwają między zadaniami, ponieważ wątki są ponownie wykorzystywane. Jeśli nie zostaną wyczyszczone, dane jednego zadania przedostają się do kolejnych zadań wykonywanych w tym samym wątku.

private static final ThreadLocal<MyContext> CTX = new ThreadLocal<>();
// Always clean up after each task:
try {
    CTX.set(new MyContext(requestId));
    doWork();
} finally {
    CTX.remove(); // prevents leak in pooled threads
}

Wycieki classloaderów w aplikacjach wdrażanych dynamicznie

W kontenerach serwletów każde wdrożenie korzysta z nowego classloadera. Jeśli dowolna klasa przechowuje statyczną referencję do klasy ze starego classloadera, cały stary classloader wraz ze wszystkimi należącymi do niego klasami nie może zostać zebrany przez GC.

Wykrywanie wycieków za pomocą zrzutów sterty

Należy wykonać zrzut sterty za pomocą jmap -dump:format=b,file=heap.hprof <pid>, a następnie otworzyć go w Eclipse MAT lub VisualVM, aby znaleźć obiekty zajmujące najwięcej pamięci oraz ich korzenie GC.

// Take a heap dump:
jmap -dump:live,format=b,file=heap.hprof $(jps | grep MyApp | cut -d" " -f1)
// Or trigger on OOM:
// -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heap.hprof

Eclipse Memory Analyzer (MAT)

Raport „Leak Suspects” w MAT automatycznie identyfikuje obiekty zajmujące dużo pamięci utrzymywanej i pokazuje ścieżkę od korzeni GC. Należy rozpocząć od widoku „Dominator Tree”, aby znaleźć obiekty będące głównymi przyczynami problemu.

Używanie WeakReference do unikania wycieków

Należy opakować obiekty przechowywane w pamięci podręcznej lub listenery w WeakReference. GC może je odzyskać w warunkach presji pamięci. Zawsze należy sprawdzać, czy wynik get() nie jest równy null.

Map<String, WeakReference<Image>> imageCache = new HashMap<>();
imageCache.put("logo", new WeakReference<>(loadImage("logo.png")));
Image logo = imageCache.get("logo") != null ? imageCache.get("logo").get() : null;
if (logo == null) logo = reload("logo.png"); // re-load if GCed

Ograniczone pamięci podręczne z LRU w LinkedHashMap

Należy przesłonić removeEldestEntry w klasie LinkedHashMap, aby ograniczyć rozmiar pamięci podręcznej i automatycznie usuwać wpis używany najdawniej.

Map<String, String> lruCache = new LinkedHashMap<>(16, 0.75f, true) {
    protected boolean removeEldestEntry(Map.Entry<String, String> e) {
        return size() > 100; // evict when over 100 entries
    }
};

Profilowanie alokacji za pomocą Java Flight Recorder

JFR (Java Flight Recorder) rejestruje profile alokacji przy minimalnym narzucie. Należy włączyć je za pomocą -XX:StartFlightRecording, a następnie przeanalizować przy użyciu JDK Mission Control.

// Start a 60-second JFR recording:
java -XX:StartFlightRecording=duration=60s,filename=rec.jfr MyApp
// Or via jcmd:
jcmd <pid> JFR.start duration=60s filename=rec.jfr

Naprawianie wycieków: lista kontrolna

Należy sprawdzić: kolekcje statyczne, niezarejestrowane listenery, ThreadLocals w pulach, wycieki połączeń lub strumieni (należy używać try-with-resources), referencje klas wewnętrznych do obiektów zewnętrznych oraz pamięci podręczne bez usuwania wpisów.

Szybkie sprawdzenie

Która flaga JVM automatycznie wykonuje zrzut sterty po zgłoszeniu OutOfMemoryError?

Podsumowanie

Typowe źródła wycieków w Javie to kolekcje statyczne, niezarejestrowane listenery, ThreadLocals w pulach oraz pamięci podręczne bez ograniczeń. Wycieków można szukać za pomocą zrzutów sterty i MAT. Należy je naprawiać przy użyciu WeakReferences, ograniczonych struktur oraz zawsze usuwać referencje, gdy przestają być potrzebne.

Często zadawane pytania

Czy lekcja „Wykrywanie i naprawianie wycieków pamięci” jest bezpłatna?

Tak — pełny tekst „Wykrywanie i naprawianie wycieków pamięci” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Wykrywanie i naprawianie wycieków pamięci”?

Identyfikuj typowe wzorce wycieków pamięci (kolekcje statyczne, listenery, pamięci podręczne) i naprawiaj je. Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Java Academy?

Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Wykrywanie i naprawianie wycieków pamięci”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?

Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Obszary sterty JVM i cykl życia obiektów
  2. Algorytmy GC: Serial, G1, ZGC, Shenandoah
  3. Wykrywanie i naprawianie wycieków pamięci
  4. Flagi dostrajania GC i profilowanie w JVisualVM
← Powrót do Java Academy