Ringkasan Kebocoran Memori Android
Tujuan manajemen memori adalah untuk membantu kami secara efektif menghindari kebocoran memori dalam aplikasi kami selama pengembangan. Setiap orang akrab dengan kebocoran memori. Sederhananya, itu berarti bahwa objek yang harus dirilis belum dirilis, dan telah dipegang oleh contoh tertentu tetapi tidak lagi digunakan, sehingga GC tidak dapat didaur ulang. Baru -baru ini, saya telah membaca banyak dokumen dan materi yang relevan. Saya berencana untuk meringkas dan menyelesaikannya dan berbagi dan belajar dengan Anda, dan juga memberi saya peringatan tentang bagaimana menghindari situasi ini selama pengkodean di masa depan dan meningkatkan pengalaman dan kualitas aplikasi.
Saya akan mulai dengan dasar -dasar kebocoran memori Java, dan menggunakan contoh spesifik untuk menggambarkan berbagai penyebab kebocoran memori Android, serta cara menggunakan alat untuk menganalisis kebocoran memori aplikasi, dan akhirnya merangkumnya.
Strategi Alokasi Memori Java
Ada tiga jenis strategi alokasi memori ketika program Java berjalan, yaitu alokasi statis, alokasi tumpukan, dan alokasi tumpukan. Sejalan dengan itu, ruang memori yang digunakan oleh tiga strategi penyimpanan terutama adalah area penyimpanan statis (juga dikenal sebagai area metode), area tumpukan dan area tumpukan.
Area penyimpanan statis (area metode): Terutama menyimpan data statis, data dan konstanta statis global. Potongan memori ini dialokasikan ketika program disusun dan ada di seluruh program yang dijalankan.
Area tumpukan: Ketika suatu metode dieksekusi, variabel lokal dalam badan metode (termasuk tipe data dasar dan referensi objek) dibuat pada tumpukan, dan memori yang dipegang oleh variabel lokal ini akan secara otomatis dirilis pada akhir eksekusi metode. Karena operasi alokasi memori tumpukan dibangun ke dalam set instruksi prosesor, itu sangat efisien, tetapi kapasitas memori yang dialokasikan terbatas.
Area Heap: Juga dikenal sebagai alokasi memori dinamis, biasanya mengacu pada memori yang secara langsung baru ketika program berjalan, yaitu, contoh objek. Ketika bagian memori ini tidak digunakan, kolektor sampah Java akan bertanggung jawab untuk didaur ulang.
Perbedaan antara tumpukan dan tumpukan:
Beberapa tipe dasar variabel yang didefinisikan dalam badan metode dan variabel referensi objek dialokasikan dalam memori tumpukan metode. Ketika suatu variabel didefinisikan dalam blok metode, Java akan mengalokasikan ruang memori untuk variabel pada tumpukan. Ketika ruang lingkup variabel terlampaui, variabel akan tidak valid, dan ruang memori yang dialokasikan untuk itu akan dibebaskan, dan ruang memori dapat digunakan kembali.
Heap Memory digunakan untuk menyimpan semua objek yang dibuat oleh baru (termasuk semua variabel anggota dalam objek) dan array. Memori yang dialokasikan dalam tumpukan akan dikelola secara otomatis oleh kolektor sampah Java. Setelah array atau objek dihasilkan dalam tumpukan, variabel khusus dapat didefinisikan dalam tumpukan. Nilai variabel ini sama dengan alamat pertama array atau objek dalam memori heap. Variabel khusus ini adalah variabel referensi yang kami sebutkan di atas. Kami dapat mengakses objek atau array di tumpukan melalui variabel referensi ini.
Misalnya:
Sampel kelas publik {int s1 = 0; sampel msample1 = sampel baru (); metode public void () {int s2 = 1; sampel msample2 = sampel baru ();}} sampel msample3 = sampel baru (); Variabel lokal S2 dari kelas sampel dan variabel referensi Msample2 keduanya ada pada tumpukan, tetapi objek yang ditunjuk oleh MSample2 ada pada heap.
Entitas objek yang ditunjuk oleh MSample3 disimpan di tumpukan, termasuk semua variabel anggota S1 dan MSample1 dari objek ini, dan ada dirinya di tumpukan.
Kesimpulan:
Jenis data dasar dan referensi variabel lokal disimpan dalam tumpukan, dan entitas objek yang direferensikan disimpan dalam tumpukan. - Karena mereka termasuk dalam variabel dalam metode, siklus hidup diakhiri dengan metode.
Variabel anggota semuanya disimpan dan dalam tumpukan (termasuk tipe data dasar, referensi dan referensi entitas objek) - karena mereka termasuk dalam kelas, objek kelas pada akhirnya akan digunakan untuk penggunaan baru.
Setelah memahami alokasi memori Java, mari kita lihat bagaimana Java mengelola memori.
Bagaimana Java Mengelola Memori
Manajemen memori Java adalah masalah alokasi dan rilis objek. Di Java, programmer perlu berlaku untuk ruang memori untuk setiap objek melalui kata kunci baru (kecuali untuk tipe dasar), dan semua objek mengalokasikan ruang dalam heap (heap). Selain itu, pelepasan objek ditentukan dan dieksekusi oleh GC. Di Java, alokasi memori dilakukan oleh program, sementara rilis memori dilakukan oleh GC. Metode pendapatan dan pengeluaran ini pada dua baris memang menyederhanakan pekerjaan pemrogram. Tetapi pada saat yang sama, itu juga menambah pekerjaan JVM. Ini juga salah satu alasan mengapa program Java berjalan lebih lambat. Karena, untuk melepaskan objek dengan benar, GC harus memantau status yang berjalan dari setiap objek, termasuk aplikasi, kutipan, kutipan, penugasan, dll. Dari objek, dan GC perlu memantau.
Memantau keadaan suatu objek adalah untuk melepaskan objek secara lebih akurat dan tepat waktu, dan prinsip dasar melepaskan objek adalah bahwa objek tidak lagi dirujuk.
Untuk lebih memahami cara kerja GC, kita dapat mempertimbangkan objek sebagai simpul dari grafik yang diarahkan, dan hubungan referensi sebagai tepi terarah dari grafik, yang titik dari referensi ke objek yang direferensikan. Selain itu, setiap objek utas dapat digunakan sebagai titik awal grafik. Sebagai contoh, sebagian besar program mulai dari proses utama, sehingga grafik adalah pohon root yang dimulai dengan titik proses utama. Dalam grafik terarah ini, objek yang dapat dijangkau oleh root vertex adalah objek yang valid, dan GC tidak akan mendaur ulang objek ini. Jika suatu objek (subgraph yang terhubung) tidak dapat dijangkau dari titik root ini (perhatikan bahwa grafik adalah grafik terarah), maka kami percaya bahwa objek ini (itu) tidak lagi dirujuk dan dapat didaur ulang oleh GC.
Di bawah ini, kami memberikan contoh cara menggunakan grafik terarah untuk mewakili manajemen memori. Untuk setiap momen program, kami memiliki grafik terarah yang mewakili alokasi memori JVM. Gambar di bawah ini adalah diagram program di sebelah kiri berjalan ke baris 6.
Java menggunakan grafik terarah untuk manajemen memori, yang dapat menghilangkan masalah loop referensi. Misalnya, ada tiga objek yang merujuk satu sama lain. Selama mereka dan proses root tidak dapat dijangkau, GC juga dapat mendaur ulangnya. Keuntungan dari metode ini adalah memiliki presisi tinggi dalam mengelola memori, tetapi efisiensi rendah. Teknologi manajemen memori lain yang umum digunakan adalah menggunakan penghitung. Misalnya, model COM menggunakan metode penghitung untuk mengelola komponen. Dibandingkan dengan grafik terarah, ia memiliki garis presisi rendah (sulit untuk menangani masalah referensi melingkar), tetapi memiliki efisiensi eksekusi yang tinggi.
Apa itu kebocoran memori di java
Di Java, kebocoran memori adalah keberadaan beberapa objek yang dialokasikan, yang memiliki dua karakteristik berikut. Pertama, objek -objek ini dapat dijangkau, yaitu, dalam grafik terarah, ada jalur yang dapat dihubungkan ke mereka; Kedua, objek -objek ini tidak berguna, yaitu program tidak akan menggunakan objek -objek ini lagi di masa depan. Jika objek memenuhi kedua kondisi ini, objek ini dapat ditentukan sebagai kebocoran memori di Java, dan objek ini tidak akan didaur ulang oleh GC, tetapi menempati memori.
Dalam C ++, kebocoran memori memiliki jangkauan yang lebih besar. Beberapa objek dialokasikan ruang memori, tetapi kemudian tidak terjangkau. Karena tidak ada GC di C ++, memori ini tidak akan pernah dikumpulkan. Di Java, benda -benda yang tidak terjangkau ini didaur ulang oleh GC, sehingga pemrogram tidak perlu mempertimbangkan bagian kebocoran memori ini.
Melalui analisis, kita tahu bahwa untuk C ++, programmer perlu mengelola tepi dan simpul sendiri, sedangkan untuk programmer Java, mereka hanya perlu mengelola tepi (tidak perlu mengelola pelepasan simpul). Dengan cara ini, Java meningkatkan efisiensi pemrograman.
Oleh karena itu, melalui analisis di atas, kita tahu bahwa ada juga kebocoran memori di Java, tetapi ruang lingkupnya lebih kecil dari C ++. Karena bahasa Java menjamin bahwa objek apa pun dapat dijangkau, semua objek yang tidak dapat dijangkau dikelola oleh GC.
Untuk pemrogram, GC pada dasarnya transparan dan tidak terlihat. Meskipun kami hanya memiliki beberapa fungsi untuk mengakses GC, seperti System.gc (), yang menjalankan GC, sesuai dengan definisi spesifikasi bahasa Java, fungsi ini tidak menjamin bahwa pengumpul sampah JVM akan dieksekusi. Karena, pelaksana JVM yang berbeda dapat menggunakan algoritma yang berbeda untuk mengelola GC. Secara umum, utas GC memiliki prioritas yang lebih rendah. Ada banyak strategi bagi JVM untuk menelepon GC. Beberapa dari mereka hanya mulai bekerja ketika penggunaan memori mencapai tingkat tertentu. Beberapa mengeksekusi mereka secara teratur. Beberapa mengeksekusi GC dengan lancar, dan beberapa mengeksekusi GC dengan cara interupsi. Tetapi secara umum, kita tidak perlu peduli tentang ini. Kecuali dalam beberapa situasi tertentu, pelaksanaan GC mempengaruhi kinerja aplikasi. Misalnya, untuk sistem berbasis web real-time seperti game online, pengguna tidak ingin GC tiba-tiba mengganggu eksekusi aplikasi dan melakukan pengumpulan sampah, maka kita perlu menyesuaikan parameter GC sehingga GC dapat membebaskan memori dengan cara yang mulus, seperti menguraikan koleksi sampah menjadi serangkaian langkah kecil untuk dijalankan. JVM hotspot yang disediakan oleh Sun mendukung fitur ini.
Juga memberikan contoh khas kebocoran memori java.
Vektor v = vektor baru (10); untuk (int i = 1; i <100; i ++) {objek o = objek baru (); v.add (o); o = null; }Dalam contoh ini, kami berlaku untuk siklus objek objek dan memasukkan objek yang diterapkan ke dalam vektor. Jika kita hanya melepaskan referensi itu sendiri, vektor masih merujuk objek, sehingga objek ini tidak dapat didaur ulang untuk GC. Oleh karena itu, jika objek harus dihapus dari vektor setelah ditambahkan ke vektor, cara termudah adalah dengan mengatur objek vektor menjadi nol.
Bocor memori di java terperinci
1. Mekanisme Daur Ulang Memori Java
Terlepas dari metode alokasi memori dari bahasa apa pun, perlu untuk mengembalikan alamat nyata dari memori yang dialokasikan, yaitu, mengembalikan pointer ke alamat pertama blok memori. Objek di Java dibuat menggunakan metode baru atau refleksi. Penciptaan benda -benda ini dialokasikan di tumpukan. Semua objek dikumpulkan oleh mesin virtual Java melalui mekanisme pengumpulan sampah. Untuk melepaskan objek dengan benar, GC akan memantau status kesehatan dari setiap objek dan memantau aplikasi, kutipan, kutipan, penugasan, dll. Java akan menggunakan metode grafik terarah untuk mengelola memori untuk memantau apakah objek tersebut dapat dicapai secara real time. Jika tidak tercapai, itu akan didaur ulang, yang juga dapat menghilangkan masalah loop referensi. Dalam bahasa Java, ada dua jenis ruang memori yang menentukan apakah ruang memori memenuhi kriteria pengumpulan sampah: satu adalah untuk menetapkan nilai kosong ke objek, yang belum disebut di bawah ini, dan yang lainnya adalah menetapkan nilai baru ke objek, sehingga merealisasikan ruang memori.
2. Penyebab kebocoran memori java
Kebocoran memori mengacu pada objek yang tidak berguna terus menerus (objek yang tidak lagi digunakan) atau memori objek yang tidak berguna tidak dapat dilepaskan dalam waktu, menghasilkan pemborosan ruang memori, yang disebut kebocoran memori. Kebocoran memori terkadang tidak serius dan tidak mudah dideteksi, sehingga pengembang tidak tahu bahwa ada bocor memori, tetapi kadang -kadang bisa sangat serius dan akan mendorong Anda untuk keluar dari memori.
Apa akar penyebab kebocoran memori Java? Jika objek siklus lama memegang referensi ke objek siklus kehidupan pendek, ada kemungkinan bahwa kebocoran memori akan terjadi. Meskipun objek siklus kehidupan pendek tidak lagi diperlukan, itu tidak dapat didaur ulang karena memegang rujukannya untuk siklus lama. Ini adalah skenario di mana bocor memori terjadi di Java. Terutama ada kategori berikut:
1. Kelas koleksi statis menyebabkan kebocoran memori:
Penggunaan hashmap, vektor, dll. Kemungkinan besar terjadi pada kebocoran memori. Siklus hidup variabel statis ini konsisten dengan aplikasi. Semua objek yang mereka referensi tidak dapat dirilis karena mereka juga akan dirujuk oleh vektor, dll.
Misalnya
Vektor statis v = vektor baru (10); untuk (int i = 1; i <100; i ++) {objek o = objek baru (); v.add (o); o = null;}Dalam contoh ini, objek objek diterapkan loop dan objek yang diterapkan ditempatkan ke dalam vektor. Jika referensi itu sendiri hanya dirilis (O = null), vektor masih merujuk objek, sehingga objek ini tidak dapat didaur ulang untuk GC. Oleh karena itu, jika objek harus dihapus dari vektor setelah ditambahkan ke vektor, cara termudah adalah dengan mengatur objek vektor menjadi nol.
2. Ketika properti objek dalam koleksi dimodifikasi, metode lepas () tidak akan berfungsi.
Misalnya:
public static void main (string [] args) {set <sone> set = new Hashset <fone> (); orang p1 = orang baru ("tang monk", "pwd1", 25); orang p2 = orang baru ("sun wukong", "pwd2", 26); orang p3 = orang baru ("zhu" Bajie "," pwd3 ", 27); set.add (p1); set.add (p2); set.add (p3); System.out.println (" Ada total: "+set.size ()+" elemen! "); // Hasil: Ada total: 3 elemen! P3.setage (2); // Ubah usia P3, dan nilai kode hash yang sesuai dengan perubahan elemen P3 pada saat ini set.remove (p3); // hapus saat ini, menyebabkan bocor memori set.add (p3); // Tambahkan lagi dan berhasil ditambahkan System.out.println ("Ada:"+set.size ()+"elemen!"); // Hasil: Ada: 4 elemen secara total! untuk (orang orang: set) {System.out.println (orang);}}3. Pendengar
Dalam pemrograman Java, kita semua perlu berurusan dengan pendengar. Biasanya, banyak pendengar digunakan dalam suatu aplikasi. Kami akan memanggil metode kontrol seperti AddXXXListener () untuk menambahkan pendengar, tetapi seringkali saat merilis objek, kami tidak ingat untuk menghapus pendengar ini, sehingga meningkatkan kemungkinan kebocoran memori.
4. Berbagai koneksi
Misalnya, koneksi basis data (DataSourse.getConnection ()), koneksi jaringan (soket) dan koneksi IO tidak akan secara otomatis didaur ulang oleh GC kecuali secara eksplisit memanggil metode tutupnya () untuk menutup hubungannya. Hasil dan objek pernyataan tidak dapat didaur ulang secara eksplisit, tetapi koneksi harus didaur ulang secara eksplisit karena koneksi tidak dapat secara otomatis didaur ulang kapan saja. Setelah koneksi didaur ulang, hasil dan objek pernyataan akan segera nol. Namun, jika Anda menggunakan kumpulan koneksi, situasinya berbeda. Selain secara eksplisit menutup koneksi, Anda juga harus secara eksplisit menutup objek pernyataan hasil (menutup salah satunya, yang lain juga akan ditutup), jika tidak, sejumlah besar objek pernyataan tidak akan dirilis, menyebabkan kebocoran memori. Dalam hal ini, koneksi biasanya akan dirilis dalam mencoba dan akhirnya.
5. Referensi ke kelas internal dan modul eksternal
Referensi ke kelas internal relatif mudah dilupakan, dan begitu mereka tidak dirilis, serangkaian objek kelas penerus mungkin tidak dirilis. Selain itu, pemrogram juga harus berhati -hati terhadap referensi yang tidak disengaja untuk modul eksternal. Misalnya, Programmer A bertanggung jawab untuk Modul A dan memanggil metode modul B seperti:
Public Void Registermsg (Objek B);
Panggilan semacam ini membutuhkan perhatian besar. Ketika suatu objek dilewatkan, sangat mungkin bahwa Modul B akan tetap merujuk pada objek. Pada saat ini, Anda perlu memperhatikan apakah Modul B menyediakan operasi yang sesuai untuk menghapus referensi.
6. Mode Singleton
Penggunaan pola singleton yang salah adalah masalah umum yang menyebabkan kebocoran memori. Objek Singleton akan ada di seluruh siklus hidup JVM setelah inisialisasi (dalam bentuk variabel statis). Jika objek Singleton memegang referensi eksternal, maka objek ini tidak akan didaur ulang secara normal oleh JVM, menghasilkan kebocoran memori. Pertimbangkan contoh berikut:
Class A {public a () {b.getInstance (). seta (this);} ....} // class b menggunakan kelas mode singleton b {private a; private static b instance = new b (); public b () {} public b getInstance () {return instance;} public void seta (a) {this.Jelas B mengadopsi pola singleton, yang memegang referensi ke objek A, dan objek Kelas A ini tidak akan didaur ulang. Bayangkan apa yang akan terjadi jika A adalah objek atau jenis koleksi yang lebih kompleks
Ringkasan kebocoran memori umum di Android
Kelas Koleksi Kebocoran
Jika kelas pengumpulan hanya memiliki metode untuk menambahkan elemen dan tidak memiliki mekanisme penghapusan yang sesuai, memori akan ditempati. Jika kelas pengumpulan ini adalah variabel global (seperti sifat statis di kelas, peta global, dll., Yaitu, ada referensi statis atau menunjuk terakhir untuk itu sepanjang waktu), maka tidak ada mekanisme penghapusan yang sesuai, yang dapat menyebabkan memori yang ditempati oleh koleksi hanya meningkat dan tidak berkurang. Misalnya, contoh khas di atas adalah salah satu dari situasi ini. Tentu saja, kami pasti tidak akan menulis kode 2B seperti itu dalam proyek, tetapi masih mudah terjadi jika kami tidak hati -hati. Sebagai contoh, kita semua suka melakukan beberapa cache melalui hashmap, jadi kita harus lebih berhati -hati dalam situasi ini.
Kebocoran memori yang disebabkan oleh singleton
Karena sifat statis dari singleton membuat siklus hidupnya selama siklus hidup aplikasi, jika digunakan secara tidak tepat, mudah untuk menyebabkan kebocoran memori. Misalnya, contoh khas berikut,
Public Class AppManager {private static appManager instance; konteks konteks privat; private appManager (konteks konteks) {this.context = context;} public static appManager getInstance (konteks konteks) {if (instance == null) {instance = new AppManager (konteks);} return instance;}}Ini adalah pola singleton yang normal. Saat membuat singleton ini, karena suatu konteks perlu dilewati, panjang siklus hidup konteks ini sangat penting:
1. Jika konteks aplikasi dilewati saat ini, karena siklus hidup aplikasi adalah siklus hidup dari seluruh aplikasi, tidak akan ada masalah.
2. Jika konteks aktivitas dilewati saat ini, ketika aktivitas yang sesuai dengan konteks ini keluar, karena referensi ke konteks dipegang oleh objek singleton, siklus hidupnya sama dengan seluruh siklus hidup aplikasi, jadi ketika aktivitas keluar, ingatannya tidak akan didaur ulang, yang menyebabkan kebocoran.
Cara yang benar harus diubah ke yang berikut:
Public Class AppManager {Private Static AppManager Instance; Konteks Konteks Privat; Private AppManager (konteks konteks) {this.context = context.getApplicationContext (); // Context Menggunakan Aplikasi} Public Static AppManager getInstance (konteks konteks) {if (instance == null) {instance = new appManager (konteks);} return;Atau tulis dengan cara ini, dan Anda bahkan tidak perlu meneruskan konteksnya:
Tambahkan metode statis ke aplikasi Anda, getContext () mengembalikan konteks aplikasi.
...
Context = getApplicationContext (); .../*** Dapatkan Konteks Global*@Return Return Objek Konteks Global*/Konteks Statis Public GetContext () {Konteks Pengembalian;} Kelas Publik AppManager {Private Static AppManager Instance; Konteks Private Context; Private AppManager () {this.context = myApplication.getContext ();//{this. {instance = new appManager ();} return instance;}}Kelas Dalam Anonim/Kelas Dalam Non-Statis dan Benang Asinkron
Kebocoran memori yang disebabkan oleh menciptakan instance statis di kelas internal non-statis
Terkadang kita sering memulai kegiatan. Untuk menghindari berulang kali membuat sumber daya data yang sama, cara penulisan ini dapat terjadi:
MainActivity kelas publik memperluas appCompatactivity {private static testResource mReSource = null; @Overrideprotected void onCreate (bundle savedInstanceState) {super.oncreate (savedInstanceState); setContentView (r.layout.activity_main); if (mmaner = null (r.layout.activity); IF (mmaner = mmaner = mManer = mManer = mManer = mManer = mManer = mManer = mManer = mManer = mManer = mManer = mManer = mManer = mManer (r.layout.activity); TestResource ();} // ...} kelas testResource {// ...}}Ini menciptakan singleton dari kelas dalam non-statis di dalam aktivitas, dan data singleton digunakan setiap kali aktivitas dimulai. Although the repeated creation of resources is avoided, this writing will cause memory leaks, because the non-static inner class will hold references to external classes by default, and the non-static inner class will create a static instance, and the life cycle of the instance is as long as the application, which causes the static instance to always hold references to the Activity, resulting in the Activity's memory resources that cannot be recycled normally. Cara yang benar untuk melakukannya adalah:
Atur kelas dalam sebagai kelas dalam statis atau ekstrak kelas dalam dan merangkumnya menjadi singleton. Jika Anda perlu menggunakan konteks, silakan ikuti konteks yang disarankan di atas untuk menggunakan aplikasi. Tentu saja, konteks aplikasi tidak mahakuasa, sehingga tidak dapat digunakan secara acak. Di beberapa tempat, Anda harus menggunakan konteks aktivitas. Skenario aplikasi konteks aplikasi, layanan, dan aktivitas adalah sebagai berikut:
Di mana: NO1 berarti aplikasi dan layanan dapat memulai suatu aktivitas, tetapi antrian tugas tugas baru perlu dibuat. Untuk dialog, itu hanya dapat dibuat dalam aktivitas
Kelas internal anonim
Pengembangan Android sering mewarisi implementasi aktivitas/fragmen/tampilan. Pada saat ini, jika Anda menggunakan kelas anonim dan dipegang oleh utas asinkron, hati -hati. Jika tidak ada ukuran, itu pasti akan menyebabkan kebocoran.
MainActivity kelas publik memperluas aktivitas {... runnable ref1 = new myRunable (); runnable ref2 = runnable baru () {@Overridepublic void run () {}}; ...}Perbedaan antara Ref1 dan Ref2 adalah bahwa Ref2 menggunakan kelas dalam anonim. Mari kita lihat memori yang dirujuk saat runtime:
Seperti yang Anda lihat, Ref1 tidak ada yang istimewa.
Tetapi ada referensi tambahan dalam objek implementasi dari kelas anonim Ref2:
Poin referensi $ 0 ini ke MainActivity. Ini, yaitu, instance MainActivity saat ini akan dipegang oleh Ref2. Jika referensi ini diteruskan ke utas asinkron, dan utas ini dan siklus hidup aktivitas ini tidak konsisten, kebocoran aktivitas akan disebabkan.
Kebocoran memori yang disebabkan oleh penangan
Masalah kebocoran memori yang disebabkan oleh penggunaan pawang harus dikatakan paling umum. Untuk menghindari ANR, kami tidak melakukan operasi yang memakan waktu di utas utama, dan menggunakan pawang untuk menangani tugas jaringan atau merangkum beberapa panggilan balik permintaan dan API lainnya. Namun, Handler tidak mahakuasa. Jika kode pawang ditulis dengan cara standar, itu dapat menyebabkan kebocoran memori. Selain itu, kita tahu bahwa penangan, pesan, dan pesan semuanya saling terkait. Jika pesan yang dikirim oleh pawang belum diproses, pesan dan objek pawang yang dikirimnya akan dipegang oleh utas pesan.
Karena variabel Handler milik TLS (Penyimpanan Lokal), siklus hidup dan aktivitas tidak konsisten. Oleh karena itu, metode implementasi ini umumnya sulit untuk memastikan bahwa ia konsisten dengan siklus hidup pandangan atau aktivitas, sehingga mudah untuk menyebabkan rilis yang benar.
Misalnya:
SAMPLEACTIVITAS KELAS PUBLIK Memperluas Aktivitas {Private Final Handler MleakyHandler = new handler () {@Overridepublic void handlemessage (pesan msg) {// ...}@overrideprotected void onCreate (bundle saveDinstanceState) {super.onCreate (savedInstancestate (savedInstanceTate) {savedInstanceTate (savedInstance) {savedInstanceTate (savedInstance) {savedInstance (savedInstance) {savedInstance (savedInstance) {savedInstance (savedInstance) menit.mleakyhandler.postdelayed (runnable baru () {@Overridepublic void run () {/ * ... */}}, 1000 * 60 * 10); // Kembali ke aktivitas sebelumnya.finish ();}}Pesan pesan tertunda eksekusi 10 menit dinyatakan dalam sampleaktivitas, dan Mleakyhandler mendorongnya ke dalam pesan antrian pesan. Ketika aktivitas dijatuhkan oleh finish (), pesan yang menunda eksekusi tugas akan terus ada di utas utama, yang memegang referensi pawang dari aktivitas, sehingga aktivitas yang dijatuhkan oleh finish () tidak akan didaur ulang, menyebabkan kebocoran memori (karena rander di sini adalah kelas dalam non-statis).
Perbaiki: Hindari menggunakan kelas dalam non-statis dalam aktivitas. Misalnya, jika kita menyatakan pawang sebagai statis di atas, periode kelangsungan hidupnya tidak ada hubungannya dengan siklus hidup aktivitas. Pada saat yang sama, aktivitas tersebut diperkenalkan melalui referensi yang lemah untuk menghindari secara langsung melewati aktivitas sebagai konteks. Lihat kode berikut:
public class SampleActivity extends Activity {/*** Instances of static inner classes do not hold an implicit* reference to their outer class.*/private static class MyHandler extends Handler {private final WeakReference<SampleActivity> mActivity;public MyHandler(SampleActivity activity) {mActivity = new WeakReference<SampleActivity>(activity);}@Overridepublic void Handlemessage (pesan pesan) {aktivitas sampleactivity = mactivity.get (); if (aktivitas! = null) {// ...}}} final private myhandler mhandler = myhandler baru (ini);/*** instance dari kelas anonim tidak ada rujukan soktat*ke dalam kelas luar mereka ketika mereka adalah "statatic". {@Overridepublic void run () {/ * ... */}};@overrideprotected void onCreate (bundle savedInstancestate) {super.oncreate (savedInstanceestate); Aktivitas.finish ();}}Ikhtisar, disarankan untuk menggunakan kelas dalam statis + lemah. Berhati -hatilah untuk kosong sebelum digunakan.
Weakreference disebutkan sebelumnya, jadi di sini saya akan berbicara secara singkat tentang beberapa jenis referensi objek Java.
Java memiliki empat kategori referensi: Referensi yang kuat, Softreference, lemah, dan phatomreference.
Dalam pengembangan aplikasi Android, untuk mencegah overflow memori, ketika berhadapan dengan beberapa objek yang menempati memori besar dan memiliki siklus deklarasi yang panjang, referensi lembut dan teknologi referensi yang lemah dapat digunakan sebanyak mungkin.
Referensi lunak/lemah dapat digunakan bersama dengan antrian referensi (ReferenceQueue). Jika objek yang dirujuk oleh referensi lunak didaur ulang oleh pengumpul sampah, mesin virtual Java akan menambahkan referensi lunak ke antrian referensi terkait. Antrian ini memungkinkan Anda untuk mengetahui daftar daur ulang referensi lunak/lemah, sehingga membersihkan buffer yang telah gagal referensi lunak/lemah.
Misalkan aplikasi kami akan menggunakan sejumlah besar gambar default, seperti avatar default, ikon game default, dll., Yang akan digunakan di banyak tempat. Jika Anda membaca gambar setiap saat, itu akan lebih lambat karena bacaan file membutuhkan operasi perangkat keras, yang akan mengarah pada kinerja yang lebih rendah. Jadi kami mempertimbangkan cache gambar dan membacanya langsung dari memori saat dibutuhkan. Namun, karena gambar memakan banyak ruang memori dan cache banyak gambar membutuhkan banyak memori, pengecualian outofmemory mungkin lebih mungkin terjadi. Pada saat ini, kita dapat mempertimbangkan menggunakan teknik referensi lembut/lemah untuk menghindari masalah ini. Berikut ini adalah prototipe cache:
Pertama -tama tentukan hashmap dan simpan objek referensi lunak.
peta pribadi <string, softreference <bitmap>> imagecache = hashmap baru <string, softreference <itmap>> ();
Mari kita tentukan metode untuk menyimpan referensi lembut bitmap ke hashmap.
Setelah menggunakan referensi lunak, sebelum pengecualian outofmemory terjadi, ruang memori dari sumber daya gambar yang di -cache ini dapat dibebaskan, sehingga mencegah memori mencapai batas atas dan menghindari kerusakan.
Jika Anda hanya ingin menghindari terjadinya pengecualian outofmemory, Anda dapat menggunakan referensi lunak. Jika Anda lebih peduli tentang kinerja aplikasi Anda dan ingin mendaur ulang beberapa objek yang menempati lebih banyak memori sesegera mungkin, Anda dapat menggunakan referensi yang lemah.
Selain itu, Anda dapat menentukan apakah objek sering digunakan untuk menentukan apakah dipilih untuk referensi lunak atau referensi yang lemah. Jika objek dapat sering digunakan, cobalah menggunakan referensi lunak. Jika objek tidak digunakan lebih mungkin, itu dapat digunakan dengan referensi yang lemah.
Oke, terus kembali ke topik. Seperti yang disebutkan sebelumnya, buat kelas bagian dalam pawang statis dan gunakan referensi lemah untuk objek yang dipegang oleh pawang, sehingga benda -benda yang dipegang oleh pawang juga dapat didaur ulang selama daur ulang. Namun, meskipun ini menghindari kebocoran aktivitas, mungkin masih ada pesan yang tertunda dalam antrian pesan utas looper, jadi kita harus menghapus pesan dalam pesan antrian pesan selama hancurkan atau menghentikan aktivitas.
Metode berikut dapat menghapus pesan:
public final void removeCallbacks (runnable r); public final void removecallbacks (runnable r, token objek); public final void removeCallbackSandMessages (token objek); pemindahan public public final void (int apa); pemindahan public final void (int apa, objek objek);
Cobalah menghindari menggunakan variabel anggota statis
Jika variabel anggota dinyatakan statis, kita semua tahu bahwa siklus hidupnya akan sama dengan seluruh siklus hidup proses aplikasi.
Ini akan menyebabkan serangkaian masalah. Jika proses aplikasi Anda dirancang untuk menjadi residen memori, maka bahkan jika aplikasi memotong ke latar belakang, bagian memori ini tidak akan dilepaskan. Menurut mekanisme manajemen memori saat ini dari aplikasi seluler, proses latar belakang yang memperhitungkan sejumlah besar memori akan didaur ulang terlebih dahulu. Jika aplikasi ini telah melakukan perlindungan timbal balik terhadap proses, itu akan menyebabkan aplikasi sering restart di latar belakang. Ketika ponsel menginstal aplikasi Anda berpartisipasi dalam pengembangan, ponsel mengkonsumsi daya dan lalu lintas semalaman, dan aplikasi Anda harus dihapus atau diam oleh pengguna.
Perbaikan di sini adalah:
Jangan menginisialisasi anggota statis di awal kelas. Inisialisasi malas dapat dipertimbangkan.
Dalam desain arsitektur, kita harus memikirkan apakah benar -benar perlu melakukan ini dan mencoba menghindarinya. Jika arsitektur perlu dirancang seperti ini, maka Anda memiliki tanggung jawab untuk mengelola siklus hidup objek ini.
Hindari override finalize ()
1. Metode finalisasi dieksekusi pada waktu yang tidak pasti dan tidak dapat diandalkan untuk melepaskan sumber daya yang langka. Alasan waktu yang tidak pasti adalah:
Waktu ketika mesin virtual memanggil GC tidak pasti
Waktu ketika finalisasi utas daemon dijadwalkan tidak pasti
2. Metode finalisasi hanya akan dieksekusi sekali. Bahkan jika objek dibangkitkan, jika metode finalisasi telah dieksekusi, itu tidak akan dieksekusi lagi ketika itu adalah GC lagi. Alasannya adalah:
Objek yang berisi metode finalisasi menghasilkan referensi finalisasi oleh mesin virtual saat baru, dan referensi ke objek. Ketika metode finalisasi dieksekusi, referensi finalisasi yang sesuai dengan objek akan dirilis. Bahkan jika objek dibangkitkan saat ini (yaitu, merujuk objek dengan referensi yang kuat), dan kedua kalinya GC, karena referensi finalisasi tidak lagi sesuai dengannya, metode finalisasi tidak akan dieksekusi.
3. Objek yang berisi metode finalisasi harus melalui setidaknya dua putaran GC sebelum dapat dirilis.
Kebocoran memori yang disebabkan oleh sumber daya yang tidak tertutup
对于使用了BraodcastReceiver,ContentObserver,File,游标Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。
一些不良代码造成的内存压力
有些代码并不造成内存泄露,但是它们,或是对没使用的内存没进行有效及时的释放,或是没有有效的利用已有的对象而是频繁的申请新内存。
Misalnya:
Bitmap 没调用recycle()方法,对于Bitmap 对象在不使用时,我们应该先调用recycle() 释放内存,然后才它设置为null. 因为加载Bitmap 对象的内存空间,一部分是java 的,一部分C 的(因为Bitmap 分配的底层是通过JNI 调用的)。 而这个recyle() 就是针对C 部分的内存释放。
构造Adapter 时,没有使用缓存的convertView ,每次都在创建新的converView。这里推荐使用ViewHolder。
Meringkaskan
对Activity 等组件的引用应该控制在Activity 的生命周期之内; 如果不能就考虑使用getApplicationContext 或者getApplication,以避免Activity 被外部长生命周期的对象引用而泄露。
尽量不要在静态变量或者静态内部类中使用非静态外部成员变量(包括context ),即使要使用,也要考虑适时把外部成员变量置空;也可以在内部类中使用弱引用来引用外部类的变量。
对于生命周期比Activity长的内部类对象,并且内部类中使用了外部类的成员变量,可以这样做避免内存泄漏:
将内部类改为静态内部类
静态内部类中使用弱引用来引用外部类的成员变量
Handler 的持有的引用对象最好使用弱引用,资源释放时也可以清空Handler 里面的消息。比如在Activity onStop 或者onDestroy 的时候,取消掉该Handler 对象的Message和Runnable.
在Java 的实现过程中,也要考虑其对象释放,最好的方法是在不使用某对象时,显式地将此对象赋值为null,比如使用完Bitmap 后先调用recycle(),再赋为null,清空对图片等资源有直接引用或者间接引用的数组(使用array.clear() ; array = null)等,最好遵循谁创建谁释放的原则。
正确关闭资源,对于使用了BraodcastReceiver,ContentObserver,File,游标Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销。
保持对对象生命周期的敏感,特别注意单例、静态对象、全局性集合等的生命周期。
The above is a summary of the causes of memory leaks in Java introduced to you by the editor and how to avoid memory leaks (super detailed version). Saya harap ini akan membantu semua orang. Jika Anda memiliki pertanyaan, silakan tinggalkan saya pesan dan editor akan membalas Anda tepat waktu. Terima kasih banyak atas dukungan Anda ke situs web Wulin.com!