OCA Java imtahan mövzuları

Garbage Collection

Bildiyimiz kimi Java`da yaradılan bütün obyektlər heap yaddaşda saxlanılır və müəyyən yer tutur. Heap yaddaşın da müəyyən limitli həcmi olur və bu həcm dolduqda proqram OutOfMemoryError xətası verir. Ona görə də istifadə edilməyən obyektləri heap yaddaşdan silmək lazımdır. Amma bunu bizim əvəzimizə Java özü edir. Bunun üçün Java`da Garbage Collector (GC) mexanizmi var və onun da özünəməxsus işləmə alqoritmləri mövcuddur. Həmin alqoritmlər vasitəsilə istifadə edilməyən obyektlər təyin edilir və heap yaddaşdan silinir.

OCA imtahanı üçün GC alqoritmləri və onların necə işləməsini bilməyə ehtiyac yoxdur. Əsas iki şeyi bilmək lazımdır:

  • Obyektlər nə vaxt GC üçün əlçatan (eligible) olur;
  • System.gc() (və yaxud Runtime.getRuntime().gc()) metodu çağırılarkən GC sizə istifadəsiz obyektlərin silinəcəyinə zəmanət vermir.

Birinci punktdan başlayaq, obyektin GC üçün əlçatan (eligible) olması nə deməkdir? Bu o deməkdir ki, obyekt artıq istifadə edilmir və o heap yaddaşdan silinə bilər.

Bəs obyekt  GC üçün nə vaxt əlçatan olur?

O zaman ki, həmin obyektə heç bir referans işarə etmir. Tutaq ki, bizim Test adlı classımız var. Bu classın obyektini yaradaq və baxaq görək bu obyekt hansı hallarda GC üçün əlçatan ola bilər:

public class Test {
    public static void main(String[] args) {
        Test t1 = new Test();
        // line6
    } 
}

1-ci hal – əgər line6 sətrinə t1 = null; ifadəsini əlavə etsək 5-ci sətirdə yaradılan new Test(); obyekti artıq 6-cı sətirdə GC üçün əlçatan (eligible) olacaq. Çünki t1 referansını null etməklə onun 5-ci sətirdə yaradılan obyektlə əlaqəsini kəsirik və həmin obyektə artıq heç bir referans işarə etmədiyindən o istifadəsiz hesab edilir və eligible olur.

2-ci hal – əgər line6 sətrinə t1 = new Test(); ifadəsini əlavə etsək 5-ci sətirdə yaradılan new Test(); obyekti yenə 6-cı sətirdə GC üçün əlçatan (eligible) olacaq. Çünki t1 referansına yeni obyekt mənimsətməklə onun köhnə obyektlə əlaqəsini kəsirik. Köhnə obyektə artıq heç bir referans işarə etmədiyindən eligible hesab edilir.

3-cü hal – əgər line6 sətrinə heç nə yazmasaq o zaman 5-ci sətirdə yaradılan obyekt 7-ci sətirdə GC üçün əlçatan (eligible) olacaq. Çünki bütün dəyişənlərin/obyektlərin həyat dövrü (təsir dairəsi) olur. Metod daxilində yaradılan bütün lokal dəyişənlərin həyat dövrü metod bitdikdə bitir. Ümumiyyətlə, dəyişənlərin/obyektlərin həyat dövrü yaradıldığı sətirdə başlayır və daxilində mövcud olduğu kod bloku bitəndə bitir. Ola bilər ki, obyekt metodun daxilində mövcud olan hər hansı bir kod blokunun içərisində yaradılsın. Bu zaman həmin obyektin həyat dövrü metod bitəndə deyil, həmin kod bloku bağlanıldıqda bitəcək.

Nümunələr üzərindən baxaq, o zaman daha aydın olacaq. İlk öncə haşiyəyə çıxaraq bir məqamı qeyd edim ki, GC ilə bağlı suallar ilk vaxtlar mənim üçün də qaranlıq idi. Gələn “feedback”lərdən başa düşürəm ki, imtahana hazırlaşan bir çox insan üçün də bu mövzu çətin və qarışıq gəlir. Əslində bu mövzuya aid sualların tapılması üçün çox sadə yanaşma üsulu var; bunun üçün sizə sadəcə kağız və qələm lazımdır. Bu vasitələrin köməkliyi ilə GC suallarını çox rahatlıqla tapmaq mümkündür. Nümunəmizə baxaq və sonra kağız-qələm ilə necə rahat tapa bilərik görək:

public class MyClass {
    public Object getObject() {
        Object obj = new String("object");
        Object arr[] = new Object[1];
        arr[0] = obj;
        obj = null;
        arr[0] = null;
        return obj;
    }
}

5-ci sətirdə yaradılmış obyekt GC üçün nə vaxt eligible olacaq?

A. 6-сı sətirdən sonra
B. 7-сi sətirdən sonra
C. 8-сi sətirdən sonra
D. 9-сu sətirdən sonra
E. 10-сu sətirdən sonra

Addım-addım baxaq. Deməli, 5-ci sətirdə yeni String obyekti yaradılır və obj referansı həmin obyektə işarə edir. 6-cı sətirdə 1 elementdən ibarət Object tipində massiv yaradılır. 7-ci sətirdə isə həmin massivin 0-cı indeksinə 5-ci sətirdə yaratdığımız obyekt mənimsədilir. Deməli, artıq 7-ci sətirdən etibarən 5-ci sətirdə yaratdığımız new String("object") obyektinə iki referans işarə edir: objarr[0]. Bu məqama diqqət etmək lazımdır, çünki sualımızın cavabına birbaşa təsir edir.

Artıq 8-ci sətirdə obj referansına null mənimsədilir. Yuxarıda “1-ci hal”-da qeyd etdik ki, əgər referansa null mənimsədilirsə, onun obyektlə əlaqəsi kəsilir. Deməli, 8-ci sətirdən etibarən obj referansı 5-ci sətirdə yaradılan String obyektinə işarə etmir. O zaman sual yaranır:

Bəs əgər obj referansı String obyektinə işarə etmirsə, həmin obyekt 8-ci sətirdən etibarən GC üçün eligible olurmu?

Cavab: xeyr. Çünki biz qeyd etmişdik ki, obyekt o zaman GC üçün eligible olur ki, həmin obyektə heç bir referans işarə etməsin. Amma 7-ci sətirdə gördük ki, arr[0] referansı da 5-ci sətirdə yaradılan String obyektinə işarə edir. 8-ci sətirdə obj referansı null edilsə də arr[0] referansı hələ də həmin String obyektinə işarə edir. Bu səbəbdən də 8-ci sətirdə həmin obyekt eligible olmur.

Artıq 9-cu sətirdə arr[0] referansı da null edilir. Bu o deməkdir ki, artıq 9-cu sətirdən etibarən 5-ci sətirdə yaradılmış String obyektinə heç bir referans işarə etmir. Məhz bu səbəbdən də 9-cu sətirdən sonra həmin obyekt eligible olur. Cavab: D.

İndi isə kağız-qələm istifadə edərək necə rahat tapmaq olar baxaq. Mən adətən bunu aşağıdakı kimi bir cizgi cızıb tapıram:

Addım 1:

gc-step1

Addım 2:

gc-step2

Addım 3:

gc-step3

Addım 4:

gc-step4

Addım 5:

gc-step5

Gördüyünüz kimi line 9`dan sonra line 5`dəki obyektə heç bir referans işarə etmir.

 

Başqa bir nümunəyə baxaq:

public class MyClass {
    public static MyClass getResult() {
        MyClass name = new MyClass();
        return name;
    }

    public static void main(String[] args) {
        MyClass result;
        result = getResult();
        System.out.println(result);  // MyClass@4e25154f
        result = null;
    }
}

5-ci sətirdə yaradılmış obyekt GC üçün nə vaxt eligible olacaq?

Yuxarıda “3-cü hal”-da qeyd etmişdik ki, metod daxilində yaradılan bütün lokal dəyişənlərin/obyektlərin həyat dövrü metod bitdikdə bitir. Bu qayda ilə yanaşdıqda artıq 7-ci sətirdə, 5-ci sətirdə yaradılmış new MyClass(); obyekti eligible olmalı idi. Amma burada maraqlı istisna var. Sətir 6-da name referansının kopyası getResult() metodu vasitəsilə sətir 10-dakı result referansına mənimsədilir. Bu isə dolayı yolla o deməkdir ki, result referansı sətir 5-də yaradılmış MyClass obyektinə işarə edir. Bu səbəbdən də həmin obyekt sətir 7-də eligible olmur. Amma sətir 12-də artıq result referansına null mənimsədilir və onun 5-ci sətirdə yaradılmış obyektlə əlaqəsi kəsilir. Buna görə də sətir 12-dən sonra 5-ci sətirdə yaradılmış obyekt eligible olur.

İndi isə System.gc() metodu ilə bağlı situasiyalara baxaq. Əvvəla qeyd edim ki, biz Garbage Collector`u birbaşa çağıra bilmərik, System.gc() metodunu çağırmaq o demək deyil ki, GC mütləq işə düşəcək. Biz bu metodu çağırmaqla sadəcə JVM`ə bununla bağlı istək göndəririk, ancaq bu bizə heç cür zəmanət vermir ki, GC işə düşəcək. GC`nin özünün xüsusi alqoritmləri var, həmin vaxt özü qərar verir ki, işə düşsün ya yox. Ona görə də System.gc() metodunun aşkar şəkildə proqramçı tərəfindən çağırılması məsləhət görülmür.

İmtahanla bağlı bilməli olduğunuz digər metod isə finalize() metodudur. Bu metod Object classına aiddir və onun necə işləməsini görmək üçün override etməyiniz lazımdır. Bəs bu metod necə və nə zaman çağırılır? finalize() metodu GC tərəfindən eligible obyektlər mövcud olarsa çağırılır. Sonuncu kod nümunəsi üzərində bir az dəyişiklik edək və bu metodun necə işləməsinə baxaq:

public class MyClass {

    String name;

    public MyClass(String name){
        this.name = name;
    }

    public static MyClass getResult() {
        MyClass name = new MyClass("Mushfiq");
        return name;
    }

    public static void main(String[] args) {
        MyClass result;
        result = getResult();
        System.out.println(result);
        result = new MyClass("Alixan");
        System.out.println(result);
        result = null;
        System.gc();
    }

    public String toString() {
        return name;
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println(this + " object is garbage collected.");
    }
}

Output:

Mushfiq
Alixan
Alixan object is garbage collected.
Mushfiq object is garbage collected.

Əgər biz main metodun daxilində System.gc(); metodunu aşkar çağırmasaq o zaman finalize() metodu da çağırılmayacaq. Koddan da göründüyü kimi bizim iki eligible obyektimiz var və buna görə də finalize() metodu iki dəfə çağırılır. Bir vacib məqamı da qeyd edim, finalize() metodu eyni obyekt üçün ancaq və ancaq bir dəfə çağırılır. Amma eyni class üçün bir neçə dəfə çağırıla bilər. finalize() ilə bağlı başqa bir çaşdırıcı suala da baxaq:

public class Test {
    public static void main(String[] args) {
        Test t1 = new Test();
        Test t2 = new Test();
        t1 = t2; t2 = null;
        // garbage collection runs
        t1 = null;
    }

    protected void finalize(Object obj){
        System.out.println("finalize method runs");
    }
}

Comment sətirində garbage collection işə düşdükdə nə baş verəcək?

A. "finalize method runs" heç vaxt çap edilməyəcək
B. "finalize method runs" bir dəfə çap ediləcək
C. "finalize method runs" iki dəfə çap ediləcək
D. Kod compile olunmur, xəta var

Ağıla ilk B variantı gəlir, çünki comment sətirinədək cəmi bir eligible obyekt var (bu arada bunu kağız-qələmlə çox rahat təyin edə bilərsiniz). Amma kodda “trick” var, bu finalize() metodunun override olunmuş versiyası deyil, finalize() metodu parametr qəbul etmir. Bu overload metoddur, buna da icaze verilir, yəni kod compile xətası vermir. Ona görə də cavab A variantıdır. Əgər finalize() metodu düzgün override olunsa idi cavab B variantı olacaqdı.

GC ilə bağlı çox qarışıq, “beyin yandıran” suallar da mövcuddur, amma inanmıram bu tip suallar imtahana salınsın. Həmin suallardan biri ilə aşağıdakı linkdən tanış ola bilərsiniz:

https://coderanch.com/t/644245/certification/CH-objects-eligible-garbage-collection

Bu arada qeyd edim ki, static dəyişənlərin həyat dövrü proqram başlayanda başlanır və proqram bitəndə bitir. Ona görə də onlar eligible olmur.

 

[topics lang=az]

 

About the author

Mushfiq Mammadov

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.

 

This site uses Akismet to reduce spam. Learn how your comment data is processed.