CodeGym /Kurslar /JAVA 25 SELF /Yaddaşla işdə tipik səhvlərin təhlili

Yaddaşla işdə tipik səhvlərin təhlili

JAVA 25 SELF
Səviyyə , Dərs
Mövcuddur

1. Yaddaşla işləyərkən tipik səhvlər

Avtomatik yaddaş idarəçiliyinin sehrinin o biri tərəfinə baxmağın vaxtıdır. C dilində hər baytı özün izləməsəniz belə, Java‑da elə «səhv etmək» olar ki, tətbiq yaddaşı ac tək «yeyər» və işləməyi ləngidər. Gəlin ən geniş yayılmış səhvləri və onlardan necə qaçmağı nəzərdən keçirək.

Unudulmuş dinləyicilər (listeners)

Java‑da tez‑tez «dinləyici» nümunəsi istifadə olunur — başqa obyektin hadisələrinə abunə olan obyekt. Məsələn, düymə yaradırsınız və ona klik emalçısı əlavə edirsiniz:

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        // klik hadisəsinin emalı
    }
});

Problem: əgər bu dinləyicini (removeActionListener) düymə və ya pəncərə artıq lazım olmayanda silməyi unutmusunuzsa, dinləyici yaddaşda asılı qalacaq. Pəncərəni bağlasanız və ona bütün istinadları null etsəniz belə, dinləyici‑obyekt hələ də pəncərəyə (və ya əksinə) istinadı saxlayır və Garbage Collector‑un yaddaşı azad etməsinə mane olur.

Analogiya: Təsəvvür edin ki, köçmüsünüz, amma pizzaxananın göndərişlərindən çıxmağı unutmusunuz — sizə hələ də köhnə ünvana reklam gəlir.

Təmizlənməyən statik kolleksiyalar

Statik sahələr sinif qədər (bəzən də tətbiqin sonunadək) yaşayır. Məsələn, statik kolleksiyanız var:

public class Cache {
    public static final List<String> globalList = new ArrayList<>();
}

və ora obyektlər əlavə edir, amma silmirsinizsə, onlar yaddaşda əbədi qalacaqlar. Hətta həmin obyektlərə başqa yerdən istinad olmasa belə, statik kolleksiyadakı istinad GC‑nin onları silməsinə imkan verməyəcək.

Real nümunə: Masaüstü tətbiqdə foto keş heç vaxt təmizlənmir. Bir neçə saat işlədikdən sonra — OutOfMemoryError.

Resursların azad edilməməsi (fayllar, axınlar, bağlantılar)

Java yaddaşı azad etsə də, fayl deskriptorlarının, şəbəkə bağlantılarının və digər xarici resursların avtomatik bağlanması ilə məşğul olmur. Faylı və ya axını bağlamağı unutduqda, resurs asılı qalır və bir anda sistem deyəcək: «Bu qədər, daha fayl verməyəcəyəm!» (IOException: Too many open files).

Məsləhət: Həmişə try-with-resources istifadə edin:

try (FileInputStream in = new FileInputStream("data.txt")) {
    // faylı oxuyuruq
} // in.close() avtomatik çağırılacaq!

Yaddaşda uzun müddət qalan böyük obyektlər

Bəzən böyük massiv və ya kolleksiya yaradırsınız, istifadə edirsiniz, sonra isə «buraxmağı unudursunuz». Məsələn:

List<byte[]> bigList = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
    bigList.add(new byte[1024 * 1024]); // hər biri 1 MB
}
// ... bigList-i təmizləməyi unutduq

Əgər bu kolleksiya statik sahədədirsə və ya uzunömürlü obyektdə saxlanılırsa, həmin yaddaşın hamısı məşğul qalacaq.

Daxili və anonim siniflər: xarici istinadların tutulması

Anonim (və daxili) siniflər Java‑da xarici obyektə qeyri‑aşkar istinad saxlayır:

public class Outer {
    void doSomething() {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello from inner!");
            }
        };
        // r haradasa saxlanılır
    }
}

Əgər r obyekti statik kolleksiyaya və ya keşə düşərsə, o, Outer nümunəsinə, o artıq lazım olmasa belə, istinadı «tutacaq». Nəticə — yaddaş sızıntısı. Lambda ifadələri ilə vəziyyət bir qədər yaxşıdır, amma əgər lambda xarici sinfin sahələrindən istifadə edirsə, istinad yenə də saxlanılır.

2. Garbage Collector (GC) ilə işdə səhvlər

Məcburi çağırış System.gc()

Bir çox yeni başlayan belə düşünür: «Yaddaş bitir — System.gc() çağırım və hər şey həll olsun!». Əslində bu, JVM‑ə sadəcə bir xahişdir, dərhal yığmanı təmin etmir. Tez‑tez istifadəsi performansı kəskin pisləşdirə, uzun fasilələrə və donmalara səbəb ola bilər. Real tətbiqlərdə JVM‑ə etibar etmək daha yaxşıdır — nə zaman zibil yığmanın lazım olduğunu özü müəyyən edəcək. Yeri gəlmişkən, bəzi JVM‑lər açıq GC çağırışlarını görməməzliyə vura bilər (məsələn, -XX:+DisableExplicitGC seçimi ilə).

GC jurnallarının yox sayılması

GC jurnallarında yığmaların nə vaxt baş verdiyi, nə qədər vaxt apardığı və nə qədər yaddaşın azad edildiyi görünür. Bu jurnallara baxmasanız, problemlərin siqnallarını qaçıra bilərsiniz: uzun pauzalar, tez‑tez Full GC, yaddaş sızıntıları.

GC jurnallarını necə aktivləşdirmək olar:

java -Xlog:gc* -jar MyApp.jar

və ya köhnə JVM‑lər üçün:

java -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar

Tapşırıq üçün GC‑nin yanlış seçimi

Yığıcının seçimi gecikmələrə və sabitliyə təsir edir. Aşağı gecikmə tələbi olan hallar üçün (birjalar, onlayn‑oyunlar) paralel stop‑the‑world Parallel GC — pis fikirdir: yığma zamanı bütün axınları «dondura» bilər. G1 GC, ZGC və ya Shenandoah nəzərdən keçirilməlidir.

3. Kolleksiyalarla bağlı səhvlər

WeakHashMap əvəzinə keşlər üçün HashMap istifadə edilməsi.
Əgər keşdə obyektlərə «canlı» istinad qalmadıqda onların avtomatik silinməsi lazımdırsa, WeakHashMap istifadə edin:

Map<Key, Value> cache = new WeakHashMap<>();

Adi HashMap ilə obyektlər keş əl ilə təmizlənənə qədər yaşayacaq, bu isə yaddaş sızıntısına gətirib çıxaracaq.

Elementlər üçün unudulmuş remove().
Kolleksiyalara (məsələn, dinləyici siyahılarına) obyektlər əlavə edir, amma onlar artıq lazım olmayanda silmirsinizsə, həmin obyektlər, xüsusən də uzunömürlü kolleksiyalarda (məsələn, statik olanlarda), əbədi yaşayacaq.

4. Best practices: problemlərdən necə qaçmaq olar

Dinləyiciləri həmişə silin.
Obyekt hadisələrə abunə olubsa, artıq lazım olmayanda onu mütləq çıxarın. Bunu dispose() metodunda və ya pəncərə/ekran bağlanarkən etmək rahatdır.

button.removeActionListener(myListener);

Keşlər üçün zəif istinadlardan istifadə edin.
Keşdə obyektin saxlanmasına zəmanət tələb olunmursa, WeakReference və ya onun əsasında qurulan kolleksiyalardan (WeakHashMap) istifadə edin. Beləcə, GC yaddaşa ehtiyac olduqda onu azad edə biləcək.

İstehsalda yaddaşı izləyin.
jvisualvm, jconsole və ya APM sistemlərindən istifadə edin. Bu, istifadəçilər şikayət etməmişdən əvvəl sızıntıları tutmağa kömək edir.

Sızıntı şübhəsi olduqda heap dump‑ı təhlil edin

Əgər tətbiq adi haldan daha çox yaddaş «yeməyə» başlayıbsa, heap dump alın (məsələn, jmap və ya jvisualvm vasitəsilə) və hansı obyektlərin ən çox yer tutduğuna baxın. Çox vaxt günahkarı bir neçə dəqiqəyə tapmaq olur.

JVM parametrlərini tənzimləyin.

  • -Xmx — yığının maksimal ölçüsü
  • -Xms — yığının başlanğıc ölçüsü

Ağıllı limitlər OutOfMemoryError qarşısını almağa kömək edir və diaqnostikanı sürətləndirir.

5. Praktika: yaddaş sızıntısı olan kod nümunəsi və onun düzəldilməsi

Nümunə 1: Statik kolleksiya vasitəsilə sızıntı

public class MemoryLeakDemo {
    // Statik kolleksiya — əbədi yaşayır
    private static final List<byte[]> leakyList = new ArrayList<>();

    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            leakyList.add(new byte[1024 * 1024]); // hər dəfə 1 MB
            System.out.println("Əlavə olundu " + (i + 1) + " MB");
        }
        // OutOfMemoryError!
    }
}

Düzəliş: Lokal dəyişən istifadə edin və ya kolleksiyanı artıq lazım olmayanda təmizləyin.

public class MemoryLeakFixed {
    public static void main(String[] args) {
        List<byte[]> tempList = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            tempList.add(new byte[1024 * 1024]);
            System.out.println("Əlavə olundu " + (i + 1) + " MB");
        }
        // tempList = null; // Açıq şəkildə null edə bilərsiniz
        // Metoddan çıxandan sonra obyektlər GC üçün əlçatandır
    }
}

Nümunə 2: Dinləyici vasitəsilə sızıntı

public class Window {
    private final List<EventListener> listeners = new ArrayList<>();

    public void addListener(EventListener l) {
        listeners.add(l);
    }
    // removeListener metodu yoxdur!
}

Düzəliş: Dinləyicini silmək üçün metod əlavə edin və pəncərə bağlanarkən onu çağırın.

public void removeListener(EventListener l) {
    listeners.remove(l);
}

Nümunə 3: WeakHashMap əvəzinə HashMap ilə keş

Map<Object, Object> cache = new HashMap<>();
// ... obyektləri əlavə edirik

Düzəliş: WeakHashMap‑a keçin:

Map<Object, Object> cache = new WeakHashMap<>();

Yaddaşın monitorinqi üçün JVM sazlama tövsiyələri

  • GC jurnallarını aktiv edin: -Xlog:gc* və ya -XX:+PrintGCDetails
  • Yığının maksimal ölçüsünü məhdudlaşdırın: -Xmx512m
  • Keşlərdən istifadə edirsinizsə — onların ölçüsünü izləyin və mümkündürsə zəif istinadlardan istifadə edin
  • GC ilə sınaqlar aparın: -XX:+UseG1GC, -XX:+UseZGC, -XX:+UseShenandoahGC

7. Yaddaşla işləyərkən tipik səhvlər

Səhv №1: Unudulmuş dinləyicilər və abunələr. Bir obyektə dinləyici əlavə etmisiniz, amma onu silməyi unutmusunuzsa, dinləyici‑obyekt (və onun istinad etdiyi hər şey) yaddaşda qalacaq. GUI və hadisə yönümlü sistemlər üçün klassik hal. removeListener/removeActionListener istifadə edin.

Səhv №2: Təmizlənməyən statik kolleksiyalar. Statik sahələr ən uzunömürlüdür. Onlara obyektlər qoyur, amma kolleksiyanı təmizləmirsinizsə, həmin obyektlər yaddaşda həmişəlik qalacaq. Sonsuz keşlər üçün xüsusilə hiyləgərdir.

Səhv №3: Xarici resursların azad edilməməsi. Açıq axın, fayl və ya bağlantı saxlamısınız? Yaddaşı israf edir və OS limitlərinə dirənirsiniz. try-with-resources istifadə edin və resursları bağlayın.

Səhv №4: Məcburi System.gc() çağırışı. Bu panacea deyil, JVM‑ə sadəcə xahişdir. Tez‑tez pauzalara və performansın pisləşməsinə gətirir.

Səhv №5: Keşlər üçün adi kolleksiyalar. Əgər keşdəki obyektlər özləri silinməlidirsə, zəif/soft istinadlardən (WeakHashMap, SoftReference) istifadə edin. Əks halda sızıntı əldə edəcəksiniz.

Səhv №6: Xarici istinadları tutan daxili və anonim siniflər. Daxili siniflər və lambda ifadələri xarici obyektə istinadı qeyri‑aşkar şəkildə saxlaya bilər. Onları uzunömürlü kolleksiyada saxlasanız — bu sızıntıdır.

Səhv №7: GC jurnallarının nəzərə alınmaması. GC jurnallarına baxmırsınızsa, uzun pauzaları və ya tez‑tez Full GC baş verməsini bilməyəcəksiniz — istifadəçilər isə bunu ləngimələr və donmalarla biləcəklər. -Xlog:gc* və ya -XX:+PrintGCDetails aktiv edin.

1
Sorğu/viktorina
, səviyyə, dərs
Əlçatan deyil
Yaddaş və zibil yığımı
Yaddaş və zibil yığımı
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION