Bagaimana cara menemukan penggunaan memori aplikasi saya di Android?


800

Bagaimana saya dapat menemukan memori yang digunakan pada aplikasi Android saya, secara terprogram?

Saya harap ada cara untuk melakukannya. Plus, bagaimana cara mendapatkan memori bebas telepon juga?


2
Atau jika ada yang ingin tahu lebih banyak tentang semua ini maka silakan merujuk http://elinux.org/Android_Memory_Usage

Jawaban:


1008

Perhatikan bahwa penggunaan memori pada sistem operasi modern seperti Linux adalah bidang yang sangat rumit dan sulit dipahami. Bahkan kemungkinan Anda benar-benar menafsirkan angka apa pun yang Anda dapatkan sangat rendah. (Cukup banyak setiap kali saya melihat nomor penggunaan memori dengan insinyur lain, selalu ada diskusi panjang tentang apa yang sebenarnya berarti bahwa hanya menghasilkan kesimpulan yang samar-samar.)

Catatan: kami sekarang memiliki dokumentasi yang jauh lebih luas tentang Mengelola Memori Aplikasi Anda yang mencakup banyak materi di sini dan lebih mutakhir dengan keadaan Android.

Hal pertama adalah mungkin membaca bagian terakhir dari artikel ini yang memiliki beberapa diskusi tentang bagaimana memori dikelola di Android:

Perubahan API Layanan dimulai dengan Android 2.0

Sekarang ActivityManager.getMemoryInfo()adalah API tingkat tertinggi kami untuk melihat penggunaan memori secara keseluruhan. Ini sebagian besar ada untuk membantu aplikasi mengukur seberapa dekat sistem tidak memiliki memori lebih untuk proses latar belakang, sehingga perlu mulai membunuh proses yang diperlukan seperti layanan. Untuk aplikasi Java murni, ini seharusnya tidak banyak berguna, karena batas heap Java ada sebagian untuk menghindari satu aplikasi dari dapat menekankan sistem ke titik ini.

Menuju level yang lebih rendah, Anda dapat menggunakan Debug API untuk mendapatkan informasi level kernel mentah tentang penggunaan memori: android.os.Debug.MemoryInfo

Catatan dimulai dengan 2.0 ada juga API ActivityManager.getProcessMemoryInfo,, untuk mendapatkan informasi ini tentang proses lain: ActivityManager.getProcessMemoryInfo (int [])

Ini mengembalikan struktur MemoryInfo tingkat rendah dengan semua data ini:

    /** The proportional set size for dalvik. */
    public int dalvikPss;
    /** The private dirty pages used by dalvik. */
    public int dalvikPrivateDirty;
    /** The shared dirty pages used by dalvik. */
    public int dalvikSharedDirty;

    /** The proportional set size for the native heap. */
    public int nativePss;
    /** The private dirty pages used by the native heap. */
    public int nativePrivateDirty;
    /** The shared dirty pages used by the native heap. */
    public int nativeSharedDirty;

    /** The proportional set size for everything else. */
    public int otherPss;
    /** The private dirty pages used by everything else. */
    public int otherPrivateDirty;
    /** The shared dirty pages used by everything else. */
    public int otherSharedDirty;

Tapi seperti apa perbedaan antara Pss, PrivateDirtydan SharedDirty... baik sekarang menyenangkan dimulai.

Banyak memori di Android (dan sistem Linux pada umumnya) sebenarnya dibagi di banyak proses. Jadi berapa banyak memori yang digunakan suatu proses benar-benar tidak jelas. Tambahkan paging keluar ke disk (apalagi swap yang tidak kita gunakan pada Android) dan itu bahkan kurang jelas.

Jadi jika Anda mengambil semua RAM fisik yang benar-benar dipetakan ke dalam setiap proses, dan menambahkan semua proses, Anda mungkin akan berakhir dengan angka yang jauh lebih besar daripada total RAM yang sebenarnya.

The Pssnomor adalah metrik ini menghitung kernel yang memperhitungkan berbagi memori akun - pada dasarnya setiap halaman RAM dalam proses skala oleh rasio jumlah proses lain juga menggunakan halaman tersebut. Dengan cara ini Anda dapat (secara teori) menambahkan pss di semua proses untuk melihat total RAM yang mereka gunakan, dan membandingkan pss antara proses untuk mendapatkan gambaran kasar tentang bobot relatif mereka.

Metrik menarik lainnya di sini adalah PrivateDirty, yang pada dasarnya adalah jumlah RAM di dalam proses yang tidak dapat di-paging ke disk (tidak didukung oleh data yang sama pada disk), dan tidak dibagi dengan proses lain. Cara lain untuk melihat ini adalah RAM yang akan tersedia untuk sistem ketika proses itu hilang (dan mungkin dengan cepat dimasukkan ke dalam cache dan penggunaan lainnya).

Cukup banyak SDK API untuk ini. Namun ada lebih banyak yang dapat Anda lakukan sebagai pengembang dengan perangkat Anda.

Menggunakan adb, ada banyak informasi yang bisa Anda dapatkan tentang penggunaan memori sistem yang sedang berjalan. Yang umum adalah perintah adb shell dumpsys meminfoyang akan memuntahkan banyak informasi tentang penggunaan memori dari setiap proses Java, yang berisi info di atas serta berbagai hal lainnya. Anda juga dapat menempel pada nama atau pid dari satu proses untuk melihat, misalnya adb shell dumpsys meminfo systemmemberi saya proses sistem:

** MEMINFO di pid 890 [sistem] **
                    dalvik asli total lainnya
            ukuran: 10940 7047 N / A 17987
       dialokasikan: 8943 5516 N / A 14459
            gratis: 336 1531 N / A 1867
           (Mz): 4585 9282 11916 25783
  (kotor bersama): 2184 3596 916 6696
    (Priv dirty): 4504 5956 7456 17916

 Benda
           Views: 149 ViewRoots: 4
     AppContexts: 13 Aktivitas: 0
          Aset: 4 Aset Manajer: 4
   Pengikat Lokal: 141 Pengikat Proksi: 158
Penerima Kematian: 49
 Soket OpenSSL: 0

 SQL
            heap: 205 dbFiles: 0
       numPagers: 0 tidak aktifPageKB: 0
    activePageKB: 0

Bagian atas adalah yang utama, di mana sizeukuran total dalam ruang alamat heap tertentu, allocatedadalah kb dari alokasi aktual yang menurut heap, freeadalah sisa kb yang bebas dari heap untuk alokasi tambahan, dan pssdan priv dirtysama seperti dibahas sebelumnya khusus untuk halaman yang terkait dengan masing-masing tumpukan.

Jika Anda hanya ingin melihat penggunaan memori di semua proses, Anda dapat menggunakan perintah adb shell procrank. Output dari ini pada sistem yang sama terlihat seperti:

  PID Vss Rss Pss Uss cmdline
  890 84456K 48668K 25850K 21284K system_server
 1231 50748K 39088K 17587K 13792K com.android.launcher2
  947 34488K 28528K 10834K 9308K com.android.wallpaper
  987 26964K 26956K 8751K 7308K com.google.process.gapps
  954 24300K ​​24296K 6249K 4824K com.android.phone
  948 23020K 23016K 5864K 4748K com.android.inputmethod.latin
  888 25728K 25724K 5774K 3668K zygote
  977 24100K 24096K 5667K 4340K android.process.acore
...
   59 336K 332K 99K 92K / system / bin / installd
   60 396K 392K 93K 84K / system / bin / keystore
   51 280K 276K 74K 68K / system / bin / servicemanager
   54 256K 252K 69K 64K / system / bin / debuggerd

Di sini kolom Vssdan Rsspada dasarnya berisik (ini adalah ruang alamat langsung dan penggunaan RAM dari suatu proses, di mana jika Anda menambahkan penggunaan RAM di seluruh proses Anda mendapatkan jumlah yang sangat besar).

Pssseperti yang kita lihat sebelumnya, dan Usssekarang Priv Dirty.

Hal yang menarik untuk diperhatikan di sini: Pssdan Usssedikit (atau lebih dari sedikit) berbeda dari apa yang kami lihat meminfo. Mengapa demikian? Well procrank menggunakan mekanisme kernel yang berbeda untuk mengumpulkan datanya meminfo, dan mereka memberikan hasil yang sedikit berbeda. Mengapa demikian? Jujur saya tidak tahu. Saya percaya procrankmungkin yang lebih akurat ... tapi sungguh, ini hanya meninggalkan titik: "ambil info memori yang Anda dapatkan dengan sebutir garam; sering kali butir yang sangat besar."

Akhirnya ada perintah adb shell cat /proc/meminfoyang memberikan ringkasan dari keseluruhan penggunaan memori sistem. Ada banyak data di sini, hanya beberapa angka pertama yang layak dibahas (dan yang tersisa hanya dimengerti oleh beberapa orang, dan pertanyaan saya tentang beberapa orang itu tentang mereka sering menghasilkan penjelasan yang saling bertentangan):

MemTotal: 395144 kB
MemFree: 184936 kB
Buffer: 880 kB
Tembolok: 84104 kB
SwapCached: 0 kB

MemTotal adalah jumlah total memori yang tersedia untuk kernel dan ruang pengguna (seringkali kurang dari RAM fisik sebenarnya dari perangkat, karena sebagian dari RAM itu diperlukan untuk radio, buffer DMA, dll).

MemFreeadalah jumlah RAM yang tidak digunakan sama sekali. Jumlah yang Anda lihat di sini sangat tinggi; biasanya pada sistem Android ini hanya beberapa MB, karena kami mencoba menggunakan memori yang tersedia untuk menjaga proses tetap berjalan

Cachedadalah RAM yang digunakan untuk cache sistem file dan hal-hal lain semacam itu. Sistem khas perlu memiliki 20MB atau lebih untuk ini agar tidak masuk ke kondisi halaman yang buruk; Android out of memory killer disetel untuk sistem tertentu untuk memastikan bahwa proses latar belakang dimatikan sebelum RAM yang di-cache dikonsumsi terlalu banyak oleh mereka untuk menghasilkan paging seperti itu.


1
Lihatlah pixelbeat.org/scripts/ps_mem.py yang menggunakan teknik yang disebutkan di atas untuk menunjukkan RAM yang digunakan untuk program
pixelbeat

17
Sangat bagus ditulis! Saya menulis posting tentang manajemen memori dan penggunaan alat yang berbeda untuk memeriksa penggunaan tumpukan Anda di sini macgyverdev.blogspot.com/2011/11/… jika ada yang merasa berguna.
Johan Norén

3
Apa sebenarnya dua kolom "dalvik" yang "asli"?
dacongy

1
Apa sebenarnya "pribumi" "dalvik" "lainnya"? Di aplikasi saya, "lain" sangat besar? bagaimana saya bisa menguranginya?
Landry

Saya dapat menggunakan "adb shell dumpsys meminfo", tetapi "adb shell procrank" tell me "/ system / bin / sh: procrank: not found". Saya tidak tahu. Berharap Anda dapat membantu saya.
Hugo

79

Ya, Anda dapat memperoleh informasi memori secara terprogram dan memutuskan apakah akan melakukan pekerjaan yang intensif memori.

Dapatkan Ukuran VM Heap dengan menelepon:

Runtime.getRuntime().totalMemory();

Dapatkan Memori VM yang dialokasikan dengan menelepon:

Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

Dapatkan VM Heap Size Limit dengan menelepon:

Runtime.getRuntime().maxMemory()

Dapatkan Memori Alokasi Asli dengan menelepon:

Debug.getNativeHeapAllocatedSize();

Saya membuat aplikasi untuk mengetahui perilaku OutOfMemoryError dan memonitor penggunaan memori.

https://play.google.com/store/apps/details?id=net.coocood.oomresearch

Anda bisa mendapatkan kode sumber di https://github.com/coocood/oom-research


7
Apakah runtime ini akan mengembalikan penggunaan memori oleh proses saat ini atau keseluruhan tumpukan sistem?
Mahendran

1
@mahemadhi dari metode JavaDoc of totalMemory () "Mengembalikan jumlah total memori yang tersedia untuk program yang sedang berjalan"
Alex

Ini bukan jawaban yang benar untuk pertanyaan itu. Jawabannya bukan tentang aplikasi tertentu.
Amir Rezazadeh

52

Ini adalah pekerjaan yang sedang berjalan, tapi ini yang saya tidak mengerti:

ActivityManager activityManager = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);

Log.i(TAG, " memoryInfo.availMem " + memoryInfo.availMem + "\n" );
Log.i(TAG, " memoryInfo.lowMemory " + memoryInfo.lowMemory + "\n" );
Log.i(TAG, " memoryInfo.threshold " + memoryInfo.threshold + "\n" );

List<RunningAppProcessInfo> runningAppProcesses = activityManager.getRunningAppProcesses();

Map<Integer, String> pidMap = new TreeMap<Integer, String>();
for (RunningAppProcessInfo runningAppProcessInfo : runningAppProcesses)
{
    pidMap.put(runningAppProcessInfo.pid, runningAppProcessInfo.processName);
}

Collection<Integer> keys = pidMap.keySet();

for(int key : keys)
{
    int pids[] = new int[1];
    pids[0] = key;
    android.os.Debug.MemoryInfo[] memoryInfoArray = activityManager.getProcessMemoryInfo(pids);
    for(android.os.Debug.MemoryInfo pidMemoryInfo: memoryInfoArray)
    {
        Log.i(TAG, String.format("** MEMINFO in pid %d [%s] **\n",pids[0],pidMap.get(pids[0])));
        Log.i(TAG, " pidMemoryInfo.getTotalPrivateDirty(): " + pidMemoryInfo.getTotalPrivateDirty() + "\n");
        Log.i(TAG, " pidMemoryInfo.getTotalPss(): " + pidMemoryInfo.getTotalPss() + "\n");
        Log.i(TAG, " pidMemoryInfo.getTotalSharedDirty(): " + pidMemoryInfo.getTotalSharedDirty() + "\n");
    }
}

Mengapa PID tidak dipetakan ke hasil di activityManager.getProcessMemoryInfo ()? Jelas Anda ingin menjadikan data yang dihasilkan bermakna, jadi mengapa Google membuatnya sangat sulit untuk menghubungkan hasil? Sistem saat ini bahkan tidak berfungsi dengan baik jika saya ingin memproses seluruh penggunaan memori karena hasil yang dikembalikan adalah array dari objek android.os.Debug.MemoryInfo, tetapi tidak ada objek yang benar-benar memberi tahu Anda apa yang terkait dengan pids. Jika Anda hanya memasukkan array dari semua tawaran, Anda tidak akan dapat memahami hasilnya. Seperti yang saya pahami penggunaannya, tidak ada artinya untuk melewatkan lebih dari satu pid pada satu waktu, dan kemudian jika itu masalahnya, mengapa membuatnya jadi activityManager.getProcessMemoryInfo () hanya membutuhkan array int?


2
Mereka kemungkinan dalam urutan yang sama dengan array input.
taer

2
Itu sepertinya cara yang sangat tidak intuitif untuk melakukan sesuatu. Ya, mungkin memang begitu, tetapi bagaimana mungkin OOP?
Ryan Beesley

6
API dirancang untuk efisiensi, bukan kemudahan penggunaan atau kesederhanaan. Ini bukan sesuatu yang harus disentuh oleh 99% aplikasi, jadi efisiensi adalah tujuan desain yang paling penting.
hackbod

2
Cukup adil. Saya mencoba menulis alat internal untuk melacak penggunaan memori untuk satu atau lebih aplikasi yang sedang kami tulis. Sebagai hasilnya, saya mencari cara untuk melakukan pemantauan ini sambil paling sedikit mempengaruhi proses lainnya, tetapi masih sedetail mungkin dengan hasilnya (pasca pemrosesan). Iterasi atas proses, dan kemudian membuat panggilan untuk setiap proses tampaknya tidak efisien, dengan asumsi ada beberapa overhead untuk setiap panggilan .getProcessMemoryInfo. Jika array yang dikembalikan dijamin dalam urutan yang sama dengan panggilan, saya akan memproses hasilnya secara membabi buta dan hanya mengasumsikan paritas.
Ryan Beesley

5
Ini adalah masalah kecil, tetapi untuk Log, tidak perlu menambahkan umpan baris, yang ditangani untuk Anda.
ThomasW


19

Android Studio 0.8.10+ telah memperkenalkan alat yang sangat berguna yang disebut Memory Monitor .

masukkan deskripsi gambar di sini

Apa manfaatnya untuk:

  • Menampilkan memori yang tersedia dan bekas dalam grafik, dan aktivitas pengumpulan sampah seiring waktu.
  • Dengan cepat menguji apakah kelambatan aplikasi mungkin terkait dengan acara pengumpulan sampah yang berlebihan.
  • Dengan cepat menguji apakah aplikasi mogok mungkin terkait dengan kehabisan memori.

masukkan deskripsi gambar di sini

Gambar 1. Memaksa acara GC (Pengumpulan Sampah) di Android Memory Monitor

Anda dapat memiliki banyak informasi bagus tentang konsumsi waktu nyata RAM aplikasi Anda dengan menggunakannya.


16

1) Saya rasa tidak, paling tidak dari Jawa.
2)

ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
MemoryInfo mi = new MemoryInfo();
activityManager.getMemoryInfo(mi);
Log.i("memory free", "" + mi.availMem);

1
ubah ke (ActivityManager activityManager = (ActivityManager) getSystemService (ACTIVITY_SERVICE);) alih-alih (ActivityManager activityManager = = (ActivityManager) getSystemService (ACTIVITY_SERVICE);)
Rajkamal

7

Kami menemukan bahwa semua cara standar untuk mendapatkan memori total dari proses saat ini memiliki beberapa masalah.

  • Runtime.getRuntime().totalMemory(): hanya mengembalikan memori JVM
  • ActivityManager.getMemoryInfo(), Process.getFreeMemory()dan apa pun berdasarkan /proc/meminfo- mengembalikan info memori tentang semua proses yang digabungkan (mis. android_util_Process.cpp )
  • Debug.getNativeHeapAllocatedSize()- menggunakan mallinfo()yang mengembalikan informasi tentang alokasi memori yang dilakukan oleh malloc()dan fungsi terkait saja (lihat android_os_Debug.cpp )
  • Debug.getMemoryInfo()- Melakukan pekerjaan tetapi terlalu lambat. Dibutuhkan sekitar 200ms pada Nexus 6 untuk satu panggilan. Kinerja overhead membuat fungsi ini tidak berguna bagi kami karena kami menyebutnya secara teratur dan setiap panggilan cukup terlihat (lihat android_os_Debug.cpp )
  • ActivityManager.getProcessMemoryInfo(int[])- panggilan Debug.getMemoryInfo()internal (lihat ActivityManagerService.java )

Akhirnya, kami akhirnya menggunakan kode berikut:

const long pageSize = 4 * 1024; //`sysconf(_SC_PAGESIZE)`
string stats = File.ReadAllText("/proc/self/statm");
var statsArr = stats.Split(new [] {' ', '\t', '\n'}, 3);

if( statsArr.Length < 2 )
    throw new Exception("Parsing error of /proc/self/statm: " + stats);

return long.Parse(statsArr[1]) * pageSize;

Ia mengembalikan metrik VmRSS . Anda dapat menemukan detail lebih lanjut di sini: satu , dua dan tiga .


PS Saya perhatikan bahwa temanya masih kekurangan potongan kode aktual dan sederhana tentang cara memperkirakan penggunaan memori pribadi dari proses jika kinerjanya bukan persyaratan penting:

Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
Debug.getMemoryInfo(memInfo);
long res = memInfo.getTotalPrivateDirty();

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) 
    res += memInfo.getTotalPrivateClean(); 

return res * 1024L;


1

Ada banyak jawaban di atas yang pasti akan membantu Anda tetapi (setelah 2 hari mampu dan meneliti alat memori adb) saya pikir saya dapat membantu dengan pendapat saya juga.

Seperti yang dikatakan Hackbod: Jadi, jika Anda mengambil semua RAM fisik yang benar-benar dipetakan ke dalam setiap proses, dan menambahkan semua proses, Anda mungkin akan berakhir dengan angka yang jauh lebih besar daripada total RAM yang sebenarnya. jadi tidak ada cara Anda bisa mendapatkan jumlah memori yang tepat per proses.

Tapi Anda bisa mendekatinya dengan beberapa logika..dan saya akan memberitahu caranya ..

Ada beberapa API seperti android.os.Debug.MemoryInfodan ActivityManager.getMemoryInfo()disebutkan di atas yang mungkin sudah Anda baca dan gunakan tetapi saya akan berbicara tentang cara lain

Jadi pertama-tama Anda harus menjadi pengguna root untuk membuatnya berfungsi. Masuk ke konsol dengan hak istimewa root dengan mengeksekusi sudalam proses dan mendapatkannya output and input stream. Kemudian id\n masukkan (masukkan) dalam ouputstream dan tulis untuk memproses output, Jika akan mendapatkan inputstream yang berisi uid=0, Anda adalah pengguna root.

Sekarang di sini adalah logika yang akan Anda gunakan dalam proses di atas

Ketika Anda mendapatkan ouputstream dari proses, lewati perintah Anda (procrank, dumpsys meminfo, dll ...) dengan \nalih - alih id dan dapatkan inputstreamdan baca, simpan aliran dalam byte [], char [] dll. Gunakan raw mentah..dan Anda selesai !!!!!

izin:

<uses-permission android:name="android.permission.FACTORY_TEST"/>

Periksa apakah Anda pengguna root:

// su command to get root access
Process process = Runtime.getRuntime().exec("su");         
DataOutputStream dataOutputStream = 
                           new DataOutputStream(process.getOutputStream());
DataInputStream dataInputStream = 
                           new DataInputStream(process.getInputStream());
if (dataInputStream != null && dataOutputStream != null) {
   // write id to console with enter
   dataOutputStream.writeBytes("id\n");                   
   dataOutputStream.flush();
   String Uid = dataInputStream.readLine();
   // read output and check if uid is there
   if (Uid.contains("uid=0")) {                           
      // you are root user
   } 
}

Jalankan perintah Anda dengan su

Process process = Runtime.getRuntime().exec("su");         
DataOutputStream dataOutputStream = 
                           new DataOutputStream(process.getOutputStream());
if (dataOutputStream != null) {
 // adb command
 dataOutputStream.writeBytes("procrank\n");             
 dataOutputStream.flush();
 BufferedInputStream bufferedInputStream = 
                     new BufferedInputStream(process.getInputStream());
 // this is important as it takes times to return to next line so wait
 // else you with get empty bytes in buffered stream 
 try {
       Thread.sleep(10000);
 } catch (InterruptedException e) {                     
       e.printStackTrace();
 }
 // read buffered stream into byte,char etc.
 byte[] bff = new byte[bufferedInputStream.available()];
 bufferedInputStream.read(bff);
 bufferedInputStream.close();
 }
}

logcat: hasil

Anda mendapatkan data mentah dalam satu string dari konsol alih-alih dalam beberapa contoh dari API apa pun, yang rumit untuk disimpan karena Anda harus memisahkannya secara manual .

Ini hanya mencoba, tolong sarankan saya jika saya melewatkan sesuatu

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.