Apa yang Anda tanyakan adalah pertanyaan yang cukup sulit. Meskipun Anda mungkin berpikir itu hanya satu pertanyaan, Anda sebenarnya mengajukan beberapa pertanyaan sekaligus. Saya akan melakukan yang terbaik dengan pengetahuan bahwa saya harus menutupinya dan, mudah-mudahan, beberapa orang lain akan bergabung untuk membahas apa yang mungkin saya lewatkan.
Kelas Bersarang: Pendahuluan
Karena saya tidak yakin seberapa nyaman Anda dengan OOP di Jawa, ini akan menjadi dasar beberapa. Kelas bersarang adalah ketika definisi kelas terkandung dalam kelas lain. Pada dasarnya ada dua jenis: Kelas Bersarang Statis dan Kelas Dalam. Perbedaan nyata antara ini adalah:
- Kelas Bersarang Statis:
- Dianggap "tingkat atas".
- Tidak memerlukan instance kelas yang mengandung untuk dibangun.
- Mungkin tidak merujuk anggota kelas yang mengandung tanpa referensi eksplisit.
- Memiliki masa hidup mereka sendiri.
- Kelas Bersarang Bagian Dalam:
- Selalu membutuhkan instance kelas yang mengandung untuk dibangun.
- Secara otomatis memiliki referensi implisit ke instance yang berisi.
- Dapat mengakses anggota kelas penampung tanpa referensi.
- Seumur hidup seharusnya tidak lebih dari wadah.
Pengumpulan Sampah dan Kelas Dalam
Pengumpulan Sampah bersifat otomatis tetapi mencoba untuk menghapus objek berdasarkan apakah mereka berpikir sedang digunakan. Pengumpul Sampah cukup pintar, tetapi tidak sempurna. Itu hanya dapat menentukan apakah sesuatu sedang digunakan oleh apakah ada referensi aktif ke objek.
Masalah sebenarnya di sini adalah ketika kelas batin telah dipertahankan lebih lama dari wadahnya. Ini karena referensi implisit ke kelas yang mengandung. Satu-satunya cara ini dapat terjadi adalah jika suatu objek di luar kelas yang berisi menyimpan referensi ke objek batin, tanpa memperhatikan objek yang mengandung.
Ini dapat mengarah pada situasi di mana objek dalam hidup (melalui referensi) tetapi referensi ke objek yang mengandung telah dihapus dari semua objek lainnya. Objek batin adalah, oleh karena itu, menjaga objek yang mengandung tetap hidup karena akan selalu memiliki referensi untuk itu. Masalah dengan ini adalah bahwa kecuali jika diprogram, tidak ada cara untuk kembali ke objek yang berisi untuk memeriksa apakah itu masih hidup.
Aspek yang paling penting untuk realisasi ini adalah tidak ada bedanya apakah itu dalam suatu Kegiatan atau dapat digambar. Anda harus selalu metodis saat menggunakan kelas dalam dan memastikan bahwa mereka tidak pernah hidup lebih lama dari objek wadah. Untungnya, jika itu bukan objek inti dari kode Anda, kebocorannya mungkin kecil dibandingkan. Sayangnya, ini adalah beberapa kebocoran yang paling sulit ditemukan, karena kemungkinan besar tidak diketahui sampai banyak dari mereka bocor.
Solusi: Kelas Dalam
- Dapatkan referensi sementara dari objek yang mengandung.
- Biarkan objek yang berisi menjadi satu-satunya yang menyimpan referensi berumur panjang ke objek dalam.
- Gunakan pola yang sudah ada seperti Pabrik.
- Jika kelas dalam tidak memerlukan akses ke anggota kelas yang mengandung, pertimbangkan untuk mengubahnya menjadi kelas statis.
- Gunakan dengan hati-hati, terlepas dari apakah itu dalam suatu Kegiatan atau tidak.
Kegiatan dan Pandangan: Pendahuluan
Kegiatan mengandung banyak informasi untuk dapat dijalankan dan ditampilkan. Aktivitas ditentukan oleh karakteristik bahwa mereka harus memiliki Tampilan. Mereka juga memiliki penangan otomatis tertentu. Apakah Anda menentukannya atau tidak, Kegiatan memiliki referensi implisit ke Lihat yang dikandungnya.
Agar Tampilan dapat dibuat, ia harus tahu di mana membuatnya dan apakah memiliki anak sehingga dapat ditampilkan. Ini berarti bahwa setiap Tampilan memiliki referensi ke Aktivitas (via getContext()
). Selain itu, setiap View menyimpan referensi untuk anak-anaknya (yaitu getChildAt()
). Akhirnya, setiap Tampilan menyimpan referensi ke Bitmap yang disajikan yang mewakili tampilannya.
Setiap kali Anda memiliki referensi ke suatu Kegiatan (atau Konteks Kegiatan), ini berarti bahwa Anda dapat mengikuti rantai SELURUH ke bawah hierarki tata letak. Inilah sebabnya mengapa kebocoran memori tentang Kegiatan atau Tampilan adalah masalah besar. Bisa jadi satu ton memori bocor sekaligus.
Kegiatan, Tampilan, dan Kelas Batin
Mengingat informasi di atas tentang Kelas Batin, ini adalah kebocoran memori yang paling umum, tetapi juga yang paling umum dihindari. Sementara itu diinginkan untuk memiliki kelas batin memiliki akses langsung ke anggota kelas Kegiatan, banyak yang bersedia untuk membuat mereka statis untuk menghindari masalah potensial. Masalah dengan Aktivitas dan Tampilan jauh lebih dalam dari itu.
Aktivitas yang Kebocoran, Tampilan dan Konteks Kegiatan
Semuanya bermuara pada Konteks dan Siklus Hidup. Ada peristiwa tertentu (seperti orientasi) yang akan membunuh Konteks Aktivitas. Karena begitu banyak kelas dan metode membutuhkan suatu Konteks, pengembang terkadang akan mencoba untuk menyimpan beberapa kode dengan mengambil referensi ke suatu Konteks dan menahannya. Kebetulan bahwa banyak objek yang harus kita buat untuk menjalankan Aktivitas kita harus ada di luar Activity LifeCycle untuk memungkinkan Aktivitas melakukan apa yang perlu dilakukan. Jika ada objek Anda yang memiliki referensi ke suatu Aktivitas, Konteksnya, atau salah satu Tampilannya saat dihancurkan, Anda baru saja membocorkan Kegiatan itu dan seluruh pohon View-nya.
Solusi: Aktivitas dan Tampilan
- Hindari, bagaimanapun caranya, membuat referensi Statis ke Tampilan atau Aktivitas.
- Semua referensi untuk Konteks Kegiatan harus berumur pendek (durasi fungsi)
- Jika Anda membutuhkan Konteks yang berumur panjang, gunakan Konteks Aplikasi (
getBaseContext()
atau getApplicationContext()
). Ini tidak menyimpan referensi secara implisit.
- Atau, Anda dapat membatasi penghancuran suatu Kegiatan dengan mengesampingkan Perubahan Konfigurasi. Namun, ini tidak menghentikan potensi peristiwa lain untuk menghancurkan Aktivitas. Meskipun Anda bisa melakukan ini, Anda mungkin masih ingin merujuk pada praktik di atas.
Runnables: Pendahuluan
Runnables sebenarnya tidak terlalu buruk. Maksud saya, mereka mungkin saja, tetapi sebenarnya kita sudah mencapai sebagian besar zona bahaya. Runnable adalah operasi asinkron yang menjalankan tugas secara independen dari utas yang dibuatnya. Sebagian besar runnables dibuat dari utas UI. Intinya, menggunakan Runnable adalah membuat utas lain, hanya sedikit lebih terkelola. Jika Anda mengklasifikasikan Runnable seperti kelas standar dan mengikuti panduan di atas, Anda akan mengalami beberapa masalah. Kenyataannya adalah bahwa banyak pengembang tidak melakukan ini.
Karena mudah, mudah dibaca, dan alur program logis, banyak pengembang memanfaatkan Kelas Batin Anonim untuk menentukan Runnables mereka, seperti contoh yang Anda buat di atas. Ini menghasilkan contoh seperti yang Anda ketikkan di atas. Anonim Inner Class pada dasarnya adalah Inner Class diskrit. Anda tidak perlu membuat definisi yang sama sekali baru dan cukup mengganti metode yang sesuai. Dalam semua hal lain itu adalah Kelas Batin, yang berarti bahwa ia menyimpan referensi implisit ke wadahnya.
Runnables dan Kegiatan / Tampilan
Yay! Bagian ini bisa pendek! Karena kenyataan bahwa Runnables berjalan di luar utas saat ini, bahaya dengan ini datang ke operasi asinkron berjalan lama. Jika runnable didefinisikan dalam Kegiatan atau Lihat sebagai Kelas Batin Anonim ATAU Kelas Batin bersarang, ada beberapa bahaya yang sangat serius. Ini karena, seperti yang dinyatakan sebelumnya, ia harus tahu siapa wadahnya. Masukkan perubahan orientasi (atau system kill). Sekarang cukup merujuk kembali ke bagian sebelumnya untuk memahami apa yang baru saja terjadi. Ya, teladan Anda cukup berbahaya.
Solusi: Runnables
- Coba dan rentangkan Runnable, jika itu tidak merusak logika kode Anda.
- Lakukan yang terbaik untuk membuat Runnables yang diperluas menjadi statis, jika harus bersarang kelas.
- Jika Anda harus menggunakan Runnables Anonim, hindari membuatnya di objek apa pun yang memiliki referensi jangka panjang untuk suatu Aktivitas atau Tampilan yang sedang digunakan.
- Banyak Runnables dapat dengan mudah menjadi AsyncTasks. Pertimbangkan untuk menggunakan AsyncTask karena itu adalah VM yang Dikelola secara default.
Menjawab Pertanyaan Akhir
Sekarang untuk menjawab pertanyaan-pertanyaan yang tidak secara langsung ditangani oleh bagian lain dari posting ini. Anda bertanya "Kapan objek kelas dalam bisa bertahan lebih lama dari kelas luarnya?" Sebelum kita membahas hal ini, izinkan saya menekankan kembali: meskipun Anda benar khawatir tentang hal ini dalam Kegiatan, ini dapat menyebabkan kebocoran di mana saja. Saya akan memberikan contoh sederhana (tanpa menggunakan Aktivitas) hanya untuk menunjukkan.
Di bawah ini adalah contoh umum dari pabrik dasar (hilang kode).
public class LeakFactory
{//Just so that we have some data to leak
int myID = 0;
// Necessary because our Leak class is an Inner class
public Leak createLeak()
{
return new Leak();
}
// Mass Manufactured Leak class
public class Leak
{//Again for a little data.
int size = 1;
}
}
Ini bukan contoh yang umum, tetapi cukup sederhana untuk diperagakan. Kuncinya di sini adalah konstruktor ...
public class SwissCheese
{//Can't have swiss cheese without some holes
public Leak[] myHoles;
public SwissCheese()
{//Gotta have a Factory to make my holes
LeakFactory _holeDriller = new LeakFactory()
// Now, let's get the holes and store them.
myHoles = new Leak[1000];
for (int i = 0; i++; i<1000)
{//Store them in the class member
myHoles[i] = _holeDriller.createLeak();
}
// Yay! We're done!
// Buh-bye LeakFactory. I don't need you anymore...
}
}
Sekarang, kami memiliki Kebocoran, tetapi tidak ada Pabrik. Meskipun kami merilis Factory, itu akan tetap di memori karena setiap Leak memiliki referensi untuk itu. Bahkan tidak masalah bahwa kelas luar tidak memiliki data. Ini terjadi jauh lebih sering daripada yang diperkirakan. Kami tidak membutuhkan pencipta, hanya ciptaannya. Jadi kami membuat sementara, tetapi gunakan kreasi tanpa batas.
Bayangkan apa yang terjadi ketika kita mengubah konstruktor sedikit.
public class SwissCheese
{//Can't have swiss cheese without some holes
public Leak[] myHoles;
public SwissCheese()
{//Now, let's get the holes and store them.
myHoles = new Leak[1000];
for (int i = 0; i++; i<1000)
{//WOW! I don't even have to create a Factory...
// This is SOOOO much prettier....
myHoles[i] = new LeakFactory().createLeak();
}
}
}
Sekarang, setiap LeakFactories baru itu baru saja bocor. Apa yang kamu pikirkan tentang itu? Itu adalah dua contoh yang sangat umum tentang bagaimana kelas batin dapat hidup lebih lama dari kelas luar dari jenis apa pun. Jika kelas luar itu adalah sebuah Kegiatan, bayangkan betapa buruknya itu.
Kesimpulan
Ini daftar bahaya yang diketahui terutama menggunakan benda-benda ini secara tidak tepat. Secara umum, pos ini seharusnya mencakup sebagian besar pertanyaan Anda, tapi saya mengerti itu adalah posting yang terlalu panjang, jadi jika Anda perlu klarifikasi, beri tahu saya. Selama Anda mengikuti praktik-praktik di atas, Anda akan sangat khawatir akan kebocoran.