Alat dan metode Android apa yang paling berhasil untuk menemukan kebocoran memori / sumber daya? [Tutup]

152

Saya memiliki aplikasi Android yang dikembangkan, dan saya berada pada titik pengembangan aplikasi telepon di mana semuanya tampak bekerja dengan baik dan Anda ingin mendeklarasikan kemenangan dan pengiriman, tetapi Anda tahu harus ada kebocoran memori dan sumber daya. di sana; dan hanya ada 16MB tumpukan di Android dan ternyata mudah bocor di aplikasi Android.

Saya sudah mencari-cari dan sejauh ini hanya mampu menggali info tentang 'hprof' dan 'traceview' dan tidak ada yang mendapat banyak ulasan yang menguntungkan.

Apa alat atau metode yang Anda temui atau kembangkan dan Anda ingin berbagi mungkin dalam proyek OS?

jottos
sumber
3
Dapatkah saya memilih agar Р̀СТȢѸ́ФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ mengubah namanya menjadi sesuatu yang dapat dibaca manusia.
JPM
1
apakah boleh untuk membuka kembali pertanyaan ini jika kita menghapus kata "Android Tools" dari judul pertanyaan? Jawaban yang ditemukan di sini cukup berguna untuk menyelesaikan masalah kebocoran memori saya
k3b
Oke, jadi saya memiliki pengguna yang terus-menerus beralih di antara aktivitas, artinya mereka mungkin telah mengganti 15 aktivitas dalam 20 detik. Mungkinkah itu penyebab kesalahan kehabisan memori? Apa yang harus saya lakukan untuk memperbaikinya? Terima kasih!
Ruchir Baronia
1
Tidak dapat memberikan ini sebagai jawaban karena pertanyaannya sudah ditutup, namun saya sarankan untuk melihat Leak Canary . Cukup gunakan aplikasi Anda, buka dan tutup kegiatan dan biarkan perpustakaan melakukan tugasnya. Ia bahkan akan memberi tahu Anda di mana kebocoran itu terjadi. Berikan saja alat analisa kebocoran waktu untuk melakukan tugasnya setelah kebocoran terjadi - biasanya membutuhkan waktu sekitar 2 menit atau lebih sampai sumber kebocoran ditemukan. Setelah itu akan disajikan kepada Anda dalam aplikasi rapi. Tidak perlu alat tambahan!
ubuntudroid

Jawaban:

90

Salah satu kesalahan paling umum yang saya temukan dalam mengembangkan Aplikasi Android adalah kesalahan "java.lang.OutOfMemoryError: Bitmap Size Exceed VM Budget". Saya menemukan kesalahan ini pada kegiatan yang menggunakan banyak bitmap setelah mengubah orientasi: Kegiatan dihancurkan, dibuat lagi dan tata letak "meningkat" dari XML yang memakan memori VM yang tersedia untuk bitmap.

Bitmap pada tata letak aktivitas sebelumnya tidak dialokasikan dengan benar oleh pengumpul sampah karena mereka telah melewati referensi ke aktivitas mereka. Setelah banyak percobaan saya menemukan solusi yang cukup bagus untuk masalah ini.

Pertama, atur atribut "id" pada tampilan induk dari tata letak XML Anda:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:id="@+id/RootView"
     >
     ...

Kemudian, pada metode onDestroy () dari Aktivitas Anda, panggil metode unbindDrawables () yang meneruskan refence ke tampilan induk dan kemudian lakukan System.gc ()

@Override
protected void onDestroy() {
    super.onDestroy();

    unbindDrawables(findViewById(R.id.RootView));
    System.gc();
}


private void unbindDrawables(View view) {

    if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
    }

    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
        }

        ((ViewGroup) view).removeAllViews();
    }
}

Metode unbindDrawables () ini mengeksplorasi pohon tampilan secara rekursif dan:

  1. Menghapus panggilan balik pada semua drawables latar belakang
  2. Menghapus childs di setiap viewgroup
hp.android
sumber
3
Solusi bagus untuk masalah umum.
While-E
9
Ini tidak berfungsi untuk subclass dari AdapterView (ListView, GridView dll).
Arjun
@Arjun Ya..itu tidak berfungsi untuk subkelas AdapterView. Untuk itu Anda perlu menangani pengecualian. Istirahat itu berfungsi dengan baik. Itulah yang saya gunakan dalam kode saya dan berfungsi dengan baik. Semoga ini membantu.
hp.android
@ hp.android Dengan "menangani ini dalam pengecualian" apakah Anda memiliki contoh seperti apa itu nantinya? Saya bertanya karena saya memiliki halaman gambar di saya PageAdapterdan saya sedang dikerjakan oleh kesalahan ini :(
Jacksonkr
4
@Jackson baru saja mengubah ketentuan: if (lihat instance dari ViewGroup &&! (Lihat instance dari AdapterView)) ini akan menghilangkan pengecualian yang Anda dapatkan untuk Adaptor
hp.android
28

Sebagian besar untuk pelancong Google dari masa depan:

Kebanyakan alat java sayangnya tidak cocok untuk tugas ini, karena mereka hanya menganalisis JVM-Heap. Setiap Aplikasi Android juga memiliki heap asli, yang juga harus masuk dalam batas ~ 16 MB. Ini biasanya digunakan untuk data bitmap, misalnya. Jadi, Anda dapat dengan mudah menjalankan kesalahan Kehabisan Memori meskipun JVM-Heap Anda adalah chillin sekitar 3 MB, jika Anda menggunakan banyak drawable.

Timo Ohr
sumber
6
mulai
drawables
@Timo lalu apa yang akan Anda gunakan untuk mendeteksi kebocoran di tumpukan asli?
sydd
1
Pengujian, banyak pengujian. Masalahnya adalah Anda bahkan tidak dapat benar-benar mengetahui berapa banyak memori yang digunakan aplikasi Anda, karena berbagi memori dan teknik pengoptimalan lainnya. Anda bisa mendapatkan pembacaan memori menggunakan perintah shell yang biasa, tetapi itu adalah perkiraan yang sangat, sangat kasar.
Timo Ohr
20

Jawaban dari @ hp.android berfungsi dengan baik jika Anda hanya bekerja dengan latar belakang bitmap tetapi, dalam kasus saya, saya telah BaseAdaptermenyediakan satu set ImageViews untuk a GridView. Saya memodifikasi unbindDrawables()metode seperti yang disarankan sehingga kondisinya adalah:

if (view instanceof ViewGroup && !(view instanceof AdapterView)) {
  ...
}

tetapi masalahnya kemudian adalah bahwa metode rekursif tidak pernah memproses anak - anak AdapterView. Untuk mengatasi ini, saya malah melakukan yang berikut:

if (view instanceof ViewGroup) {
  ViewGroup viewGroup = (ViewGroup) view;
  for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));

  if (!(view instanceof AdapterView))
    viewGroup.removeAllViews();
}

sehingga anak-anak AdapterViewmasih diproses - metode ini tidak berusaha untuk menghapus semua anak (yang tidak didukung).

Ini tidak cukup memperbaiki masalah namun karena ImageViewmengelola bitmap yang bukan latar belakang mereka. Karena itu saya menambahkan yang berikut ini. Ini tidak ideal tetapi berfungsi:

if (view instanceof ImageView) {
  ImageView imageView = (ImageView) view;
  imageView.setImageBitmap(null);
}

Secara keseluruhan unbindDrawables()metode ini adalah:

private void unbindDrawables(View view) {
  if (view.getBackground() != null)
    view.getBackground().setCallback(null);

  if (view instanceof ImageView) {
    ImageView imageView = (ImageView) view;
    imageView.setImageBitmap(null);
  } else if (view instanceof ViewGroup) {
    ViewGroup viewGroup = (ViewGroup) view;
    for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));

    if (!(view instanceof AdapterView))
      viewGroup.removeAllViews();
  }
}

Saya berharap ada pendekatan yang lebih berprinsip untuk membebaskan sumber daya semacam itu.

darrenp
sumber
12

Google I / O talk yang baik (2011) tentang Manajemen Memori di Android, serta rincian tentang alat + teknik untuk profil memori:
http://www.youtube.com/watch?v=_CruQY55HOk

greg7gkb
sumber
4
Atau posting blog yang sesuai: android-developers.blogspot.com/2011/03/...
greg7gkb
1
Oke, jadi saya memiliki pengguna yang terus-menerus beralih di antara aktivitas, artinya mereka mungkin telah mengganti 15 aktivitas dalam 20 detik. Mungkinkah itu penyebab kesalahan kehabisan memori? Apa yang harus saya lakukan untuk memperbaikinya? Terima kasih!'
Ruchir Baronia
1

Ya, itu adalah alat yang menghubungkan dengan format unik yang digunakan Android .. Saya pikir apa yang mungkin Anda tidak puas adalah kerangka kode pengujian yang mendasarinya sedang digunakan ..

Sudahkah Anda mencoba menguji area kode dengan menggunakan Android Mock Framework?

Fred Grott
sumber
1
tidak begitu banyak, pengujian yang sifatnya tidak begitu banyak masalah seperti merekam apa yang sebenarnya terjadi saat aplikasi berjalan, apa yang saya benar-benar butuhkan adalah alat profiling sumber daya / memori kebocoran
jottos