Hai!

Saya rasa anda tidak akan terlalu terkejut jika saya memberitahu anda bahawa komputer anda mempunyai jumlah memori yang terhad :) Malah cakera keras — biasanya berkali ganda lebih besar daripada storan RAM — boleh dimuatkan mengikut kapasiti dengan permainan kegemaran anda, rancangan TV, dan banyak lagi. Untuk mengelakkan perkara ini berlaku, anda perlu memantau keadaan memori semasa dan memadam fail yang tidak diperlukan dari komputer anda. Apakah kaitan pengaturcaraan Java dengan semua ini? Semuanya! Lagipun, apabila mesin Java mencipta sebarang objek, ia memperuntukkan memori untuk objek itu.

Dalam program besar sebenar, puluhan dan ratusan ribu objek dicipta, dan setiap daripada mereka mempunyai sekeping ingatan sendiri yang diperuntukkan untuknya. Tetapi berapa lama anda fikir semua objek ini wujud? Adakah mereka "hidup" sepanjang masa program kami berjalan? Sudah tentu tidak. Walaupun dengan semua kelebihan objek Java, mereka tidak kekal :) Objek mempunyai kitaran hayat mereka sendiri. Hari ini kita akan berehat sedikit daripada menulis kod dan melihat proses ini :) Selain itu, ia adalah sangat penting untuk pemahaman anda tentang cara program berfungsi dan cara sumber diuruskan. Jadi, bilakah kehidupan sesuatu objek bermula? Seperti seseorang - sejak kelahirannya, iaitu penciptaan.


Cat cat = new Cat(); // Here the lifecycle of our Cat object begins!

Pertama, Mesin Maya Java memperuntukkan jumlah memori yang diperlukan untuk mencipta objek. Kemudian ia mencipta rujukan kepada ingatan itu. Dalam kes kami, rujukan itu dipanggil cat, jadi kami boleh menjejakinya. Kemudian semua pembolehubahnya dimulakan, pembina dipanggil, dan - ta-da! — objek kami yang baru dicetak sedang menjalani kehidupannya sendiri :)

Jangka hayat objek berbeza-beza, jadi kami tidak dapat memberikan nombor yang tepat di sini. Walau apa pun, ia hidup untuk beberapa lama di dalam program dan melaksanakan fungsinya. Tepatnya, sesuatu objek adalah "hidup" selagi terdapat rujukan kepadanya. Sebaik sahaja tiada rujukan yang tinggal, objek "mati". Contoh:


public class Car {
  
   String model;

   public Car(String model) {
       this.model = model;
   }

   public static void main(String[] args) {
       Car lamborghini  = new Car("Lamborghini Diablo");
       lamborghini = null;

   }

}

Objek kereta Lamborghini Diablo berhenti hidup pada baris kedua main()kaedah itu. Terdapat hanya satu rujukan kepadanya, dan kemudian rujukan itu ditetapkan sama dengan null. Oleh kerana tiada lagi rujukan kepada Lamborghini Diablo, memori yang diperuntukkan menjadi "sampah". Rujukan tidak perlu ditetapkan kepada null untuk ini berlaku:


public class Car {

   String model;

   public Car(String model) {
       this.model = model;
   }

   public static void main(String[] args) {
       Car lamborghini  = new Car("Lamborghini Diablo");

       Car lamborghiniGallardo = new Car("Lamborghini Gallardo");
       lamborghini = lamborghiniGallardo;
   }

}

Di sini kami mencipta objek kedua dan kemudian memberikan objek baharu ini kepada lamborghinirujukan. Sekarang Lamborghini Gallardoobjek mempunyai dua rujukan, tetapi Lamborghini Diabloobjek itu tidak mempunyai satu pun. Ini bermakna bahawa Diabloobjek itu kini sampah. Ini adalah apabila mekanisme terbina dalam Java yang dipanggil pengumpul sampah (GC) mula dimainkan.

Pengumpul sampah ialah mekanisme Java dalaman yang bertanggungjawab untuk membebaskan memori, iaitu mengeluarkan objek yang tidak diperlukan daripada ingatan. Terdapat sebab kukuh mengapa kami memilih gambar pembersih vakum robot di sini. Lagipun, pemungut sampah berfungsi dengan cara yang sama: di latar belakang, ia "mengembara" tentang program anda, mengumpul sampah tanpa usaha anda. Tugasnya adalah untuk mengalih keluar objek yang tidak lagi digunakan dalam program.

Melakukan ini membebaskan memori dalam komputer untuk objek lain. Adakah anda masih ingat bahawa pada permulaan pelajaran kami mengatakan bahawa dalam kehidupan biasa anda perlu memantau keadaan komputer anda dan memadam fail yang tidak perlu? Nah, dalam kes objek Java, pengumpul sampah melakukan ini untuk anda. Pengumpul sampah berjalan berulang kali semasa program anda berjalan: anda tidak perlu memanggilnya secara eksplisit atau memberikannya arahan, walaupun ini secara teknikal mungkin. Kemudian kita akan membincangkannya dengan lebih lanjut dan menganalisis kerjanya dengan lebih terperinci.

Apabila pemungut sampah mencapai objek, sejurus sebelum memusnahkan objek, ia memanggil kaedah khas — finalize()— pada objek. Kaedah ini boleh mengeluarkan sumber lain yang digunakan oleh objek. Kaedah ini finalize()adalah sebahagian daripada Objectkelas. Ini bermakna sebagai tambahan kepada equals(), hashCode()dan toString()kaedah yang anda temui sebelum ini, setiap objek mempunyai kaedah ini. Ia berbeza daripada kaedah lain kerana ia - bagaimana saya harus meletakkan ini - sangat berubah-ubah.

Khususnya, ia tidak selalu dipanggil sebelum pemusnahan objek. Pengaturcaraan adalah usaha yang tepat. Pengaturcara memberitahu komputer untuk melakukan sesuatu, dan komputer melakukannya. Saya rasa anda sudah biasa dengan tingkah laku ini, jadi pada mulanya mungkin sukar untuk anda menerima idea berikut: "Sebelum pemusnahan objek, kaedah kelas finalize()dipanggil Object. Atau mungkin ia tidak dipanggil. Semuanya bergantung kepada nasib awak!"

Namun, ia adalah benar. Mesin Java sendiri menentukan sama ada untuk memanggil finalize()kaedah mengikut kes atau tidak. Sebagai contoh, mari cuba jalankan kod berikut sebagai percubaan:


public class Cat {

   private String name;

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

   public Cat() {
   }

   public static void main(String[] args) throws Throwable {
       for (int i = 0 ; i < 1000000; i++) {
           Cat cat = new Cat();
           cat = null; // This is when the first object becomes available to the garbage collector
       }
   }

   @Override
   protected void finalize() throws Throwable {
       System.out.println("Cat object destroyed!");
   }
}

Kami mencipta Catobjek dan kemudian dalam baris kod seterusnya kami menetapkan satu-satunya rujukannya sama dengan null. Dan kami melakukannya berjuta kali. Kami secara eksplisit mengatasi finalize()kaedah supaya ia mencetak rentetan ke konsol sejuta kali (sekali untuk setiap kali ia memusnahkan objek Cat). Tetapi tidak! Tepatnya, ia berjalan hanya 37,346 kali pada komputer saya! Iaitu, hanya sekali dalam 27 kali mesin Java yang dipasang pada mesin saya memutuskan untuk memanggil finalize()kaedah tersebut.

Dalam kes lain, kutipan sampah berlaku tanpanya. Cuba jalankan kod ini untuk diri sendiri: kemungkinan besar, anda akan mendapat hasil yang berbeza. Seperti yang anda lihat, finalize()sukar untuk dipanggil rakan kongsi yang boleh dipercayai :) Jadi, sedikit nasihat untuk masa depan: jangan bergantung pada finalize()kaedah untuk membebaskan sumber kritikal. Mungkin JVM akan memanggilnya, atau mungkin tidak. Siapa tahu?

Jika semasa objek anda masih hidup ia memegang beberapa sumber yang sangat penting untuk prestasi, contohnya, sambungan pangkalan data terbuka, adalah lebih baik untuk mencipta kaedah khas dalam kelas anda untuk melepaskannya dan kemudian memanggilnya secara eksplisit apabila objek itu tidak lagi diperlukan. Dengan cara itu anda akan tahu dengan pasti bahawa prestasi program anda tidak akan terjejas. Dari awal lagi, kami mengatakan bahawa bekerja dengan ingatan dan membuang sampah adalah sangat penting, dan ini benar. Mengendalikan sumber yang tidak betul dan salah faham bagaimana objek yang tidak perlu dibersihkan boleh menyebabkan kebocoran memori. Ini adalah salah satu kesilapan pengaturcaraan yang paling terkenal.

Jika pengaturcara menulis kod mereka secara salah, memori baharu mungkin diperuntukkan untuk objek yang baru dibuat setiap kali, manakala objek lama yang tidak diperlukan mungkin tidak tersedia untuk dialih keluar oleh pengumpul sampah. Memandangkan kami membuat analogi dengan pembersih vakum robot, bayangkan apa yang akan berlaku jika, sebelum memulakan robot, anda menabur stoking di sekeliling rumah, memecahkan pasu kaca, dan meninggalkan blok bangunan Lego di seluruh lantai. Robot akan cuba melakukan tugasnya, sudah tentu, tetapi pada satu ketika ia akan tersekat.

Untuk membolehkan pembersih vakum robot berfungsi dengan baik, anda perlu memastikan lantai dalam keadaan baik dan mengeluarkan apa-apa yang tidak dapat dikendalikan oleh robot. Prinsip yang sama berlaku untuk pengumpul sampah Java. Jika terdapat banyak objek yang tinggal dalam program yang tidak boleh dibersihkan (seperti stoking atau blok bangunan Lego untuk pembersih vakum robot kami), pada satu ketika anda akan kehabisan memori. Dan mungkin bukan sahaja program anda akan membeku — setiap program lain yang berjalan pada komputer mungkin terjejas. Mereka juga mungkin tidak mempunyai ingatan yang mencukupi.

Anda tidak perlu menghafal ini. Anda hanya perlu memahami prinsip di sebalik cara ia berfungsi.