Mendeteksi ukuran tumpukan aplikasi di Android

145

Bagaimana Anda secara terprogram mendeteksi ukuran tumpukan aplikasi yang tersedia untuk aplikasi Android?

Saya mendengar ada fungsi yang melakukan ini di versi SDK yang lebih baru. Bagaimanapun, saya mencari solusi yang berfungsi untuk 1,5 dan ke atas.

hpique
sumber

Jawaban:

453

Ada dua cara untuk memikirkan frasa "ukuran tumpukan aplikasi Anda":

  1. Berapa banyak tumpukan yang bisa digunakan aplikasi saya sebelum kesalahan yang sulit dipicu? Dan

  2. Berapa banyak tumpukan yang harus digunakan aplikasi saya, mengingat kendala dari versi OS Android dan perangkat keras dari perangkat pengguna?

Ada metode berbeda untuk menentukan masing-masing di atas.

Untuk item 1 di atas: maxMemory()

yang dapat dipanggil (misalnya, dalam onCreate()metode aktivitas utama Anda ) sebagai berikut:

Runtime rt = Runtime.getRuntime();
long maxMemory = rt.maxMemory();
Log.v("onCreate", "maxMemory:" + Long.toString(maxMemory));

Metode ini memberi tahu Anda berapa banyak total byte tumpukan yang diizinkan untuk digunakan oleh aplikasi Anda .

Untuk item 2 di atas: getMemoryClass()

yang dapat dipanggil sebagai berikut:

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
int memoryClass = am.getMemoryClass();
Log.v("onCreate", "memoryClass:" + Integer.toString(memoryClass));

Metode ini memberi tahu Anda kira-kira berapa megabita tumpukan yang harus digunakan aplikasi Anda jika ingin menghormati batas-batas perangkat saat ini dengan benar, dan tentang hak-hak aplikasi lain untuk berjalan tanpa dipaksa berulang kali ke dalam siklus onStop()/ onResume()karena mereka kasar. keluar dari memori sementara aplikasi gajah Anda mandi di jacuzzi Android.

Perbedaan ini tidak terdokumentasi dengan jelas, sejauh yang saya tahu, tetapi saya telah menguji hipotesis ini pada lima perangkat Android yang berbeda (lihat di bawah) dan telah mengkonfirmasi untuk kepuasan saya sendiri bahwa ini adalah interpretasi yang benar.

Untuk versi stok Android, maxMemory()biasanya akan mengembalikan sekitar jumlah megabyte yang sama seperti yang ditunjukkan dalam getMemoryClass()(yaitu, sekitar satu juta kali nilai terakhir).

Satu-satunya situasi (yang saya ketahui) di mana kedua metode dapat menyimpang adalah pada perangkat yang di-rooting yang menjalankan versi Android seperti CyanogenMod, yang memungkinkan pengguna untuk secara manual memilih seberapa besar ukuran heap yang diperbolehkan untuk setiap aplikasi. Dalam CM, misalnya, opsi ini muncul di bawah "Pengaturan CyanogenMod" / "Kinerja" / "VM heap size".

CATATAN: HATI-HATI YANG MENGATUR NILAI INI SECARA MANUAL AKAN DAPAT MENYESUAIKAN SISTEM ANDA, TERUTAMA jika Anda memilih nilai yang lebih kecil dari normal untuk perangkat Anda.

Berikut ini adalah hasil pengujian saya yang menunjukkan nilai yang dikembalikan oleh maxMemory()dan getMemoryClass()untuk empat perangkat berbeda yang menjalankan CyanogenMod, menggunakan dua nilai tumpukan (yang diatur secara manual) untuk masing-masing:

  • G1:
    • Dengan VM Heap Size diatur ke 16MB:
      • maxMemory: 16777216
      • getMemoryClass: 16
    • Dengan VM Heap Size diatur ke 24MB:
      • maxMemory: 25165824
      • getMemoryClass: 16
  • Moto Droid:
    • Dengan VM Heap Size diatur ke 24MB:
      • maxMemory: 25165824
      • getMemoryClass: 24
    • Dengan VM Heap Size diatur ke 16MB:
      • maxMemory: 16777216
      • getMemoryClass: 24
  • Nexus One:
    • Dengan VM Heap size diatur ke 32MB:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • Dengan VM Heap size diatur ke 24MB:
      • maxMemory: 25165824
      • getMemoryClass: 32
  • GT Viewsonic:
    • Dengan VM Heap Size diatur ke 32:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • Dengan VM Heap Size diatur ke 64:
      • maxMemory: 67108864
      • getMemoryClass: 32

Selain yang di atas, saya menguji pada tablet Novo7 Paladin yang menjalankan Ice Cream Sandwich. Ini pada dasarnya adalah versi stok ICS, kecuali bahwa saya telah melakukan rooting tablet melalui proses sederhana yang tidak menggantikan seluruh OS, dan khususnya tidak menyediakan antarmuka yang akan memungkinkan ukuran tumpukan disesuaikan secara manual.

Untuk perangkat itu, berikut hasilnya:

  • 7 November
    • maxMemory: 62914560
    • getMemoryClass: 60

Juga (per Kishore dalam komentar di bawah):

  • HTC One X
    • maxMemory: 67108864
    • getMemoryClass: 64

Dan (per komentar aliasuppi):

  • Samsung Galaxy Core Plus
    • maxMemory: (Tidak ditentukan dalam komentar)
    • getMemoryClass: 48
    • largeMemoryClass: 128

Per komentar dari cmcromance:

  • Galaxy S3 (Jelly Bean) tumpukan besar
    • maxMemory: 268435456
    • getMemoryClass: 64

Dan (per komentar Tencent):

  • LG Nexus 5 (4.4.3) normal
    • maxMemory: 201326592
    • getMemoryClass: 192
  • LG Nexus 5 (4.4.3) tumpukan besar
    • maxMemory: 536870912
    • getMemoryClass: 192
  • Galaxy Nexus (4.3) normal
    • maxMemory: 100663296
    • getMemoryClass: 96
  • Galaxy Nexus (4.3) banyak
    • maxMemory: 268435456
    • getMemoryClass: 96
  • Galaxy S4 Play Store Edition (4.4.2) normal
    • maxMemory: 201326592
    • getMemoryClass: 192
  • Galaxy S4 Play Store Edition (4.4.2) tumpukan besar
    • maxMemory: 536870912
    • getMemoryClass: 192

Perangkat lain

  • Huawei Nexus 6P (6.0.1) normal
    • maxMemory: 201326592
    • getMemoryClass: 192

Saya belum menguji kedua metode ini menggunakan android khusus: largeHeap = "true" opsi manifes yang tersedia sejak Honeycomb, tetapi berkat cmcromance dan tencent kami memiliki beberapa contoh nilai largeHeap, seperti yang dilaporkan di atas.

Harapan saya (yang tampaknya didukung oleh angka-angka besar di atas) adalah bahwa opsi ini akan memiliki efek yang mirip dengan mengatur tumpukan secara manual melalui OS yang di-rooting - yaitu, itu akan meningkatkan nilai maxMemory()saat meninggalkan getMemoryClass()sendiri. Ada metode lain, getLargeMemoryClass (), yang menunjukkan berapa banyak memori yang diperbolehkan untuk aplikasi menggunakan pengaturan largeHeap. Dokumentasi untuk getLargeMemoryClass () menyatakan, "sebagian besar aplikasi seharusnya tidak membutuhkan jumlah memori ini, dan sebaliknya harus tetap dengan batas getMemoryClass ()."

Jika saya menebak dengan benar, maka menggunakan opsi itu akan memiliki manfaat yang sama (dan bahaya) seperti menggunakan ruang yang disediakan oleh pengguna yang menaikkan tumpukan melalui OS yang di-root (yaitu, jika aplikasi Anda menggunakan memori tambahan, mungkin tidak akan bermain sebaik aplikasi apa pun yang dijalankan pengguna pada saat yang sama).

Perhatikan bahwa kelas memori rupanya tidak perlu kelipatan 8MB.

Kita dapat melihat dari atas bahwa getMemoryClass()hasilnya tidak berubah untuk konfigurasi perangkat / OS yang diberikan, sedangkan nilai maxMemory () berubah ketika heap diatur secara berbeda oleh pengguna.

Pengalaman praktis saya sendiri adalah bahwa pada G1 (yang memiliki kelas memori 16), jika saya secara manual memilih 24MB sebagai ukuran tumpukan, saya dapat berjalan tanpa kesalahan bahkan ketika penggunaan memori saya dibiarkan melayang hingga 20MB (mungkin itu bisa setinggi 24MB, meskipun saya belum mencoba ini). Tetapi aplikasi ish besar lainnya yang serupa mungkin saja memerah karena ingatan saya sendiri. Dan, sebaliknya, aplikasi saya bisa memerah dari memori jika aplikasi pemeliharaan tinggi lainnya dibawa ke latar depan oleh pengguna.

Jadi, Anda tidak dapat melebihi jumlah memori yang ditentukan oleh maxMemory(). Dan, Anda harus mencoba untuk tetap dalam batas yang ditentukan oleh getMemoryClass(). Salah satu cara untuk melakukan itu, jika semuanya gagal, mungkin untuk membatasi fungsionalitas untuk perangkat tersebut dengan cara yang menghemat memori.

Akhirnya, jika Anda berencana untuk membahas jumlah megabita yang ditentukan dalam getMemoryClass(), saran saya adalah bekerja keras dan lama dalam menyimpan dan memulihkan keadaan aplikasi Anda, sehingga pengalaman pengguna hampir tidak terganggu jika suatu onStop()/ onResume()siklus terjadi.

Dalam kasus saya, untuk alasan kinerja saya membatasi aplikasi saya ke perangkat yang menjalankan 2.2 dan di atasnya, dan itu berarti bahwa hampir semua perangkat yang menjalankan aplikasi saya akan memiliki memoryClass 24 atau lebih tinggi. Jadi saya dapat merancang untuk menempati tumpukan hingga 20MB dan merasa cukup percaya diri bahwa aplikasi saya akan bermain bagus dengan aplikasi lain yang mungkin dijalankan pengguna pada saat yang sama.

Tetapi akan selalu ada beberapa pengguna yang di-rooting yang memuat Android versi 2.2 atau di atas ke perangkat yang lebih lama (mis., G1). Ketika Anda menemukan konfigurasi seperti itu, idealnya, Anda harus mengurangi penggunaan memori Anda, bahkan jika maxMemory()memberi tahu Anda bahwa Anda bisa jauh lebih tinggi daripada 16MB yang getMemoryClass()memberi tahu Anda bahwa Anda harus menargetkan. Dan jika Anda tidak dapat memastikan bahwa aplikasi Anda akan hidup sesuai anggaran itu, maka setidaknya pastikan bahwa onStop()/ onResume()berfungsi dengan mulus.

getMemoryClass(), seperti yang ditunjukkan oleh Diane Hackborn (hackbod) di atas, hanya tersedia kembali ke API level 5 (Android 2.0), dan karena itu, seperti yang ia sarankan, Anda dapat mengasumsikan bahwa perangkat keras fisik perangkat apa pun yang menjalankan versi OS sebelumnya dirancang. untuk secara optimal mendukung aplikasi yang menempati ruang tumpukan tidak lebih dari 16MB.

Sebaliknya, maxMemory()menurut dokumentasi, tersedia sepanjang perjalanan kembali ke level API 1. maxMemory(), pada pra-2.0 versi, mungkin akan mengembalikan nilai 16MB, tapi aku lakukan melihat bahwa dalam saya (lama kemudian) versi CyanogenMod pengguna dapat memilih nilai heap serendah 12MB, yang mungkin akan menghasilkan batas heap yang lebih rendah, dan jadi saya sarankan Anda terus menguji maxMemory()nilainya, bahkan untuk versi OS sebelum 2.0. Anda bahkan mungkin harus menolak untuk menjalankan jika nilai ini tidak ditetapkan lebih rendah dari 16MB, jika Anda harus memiliki lebih dari maxMemory()indikasi yang diizinkan.

Carl
sumber
2
@Carl Halo Carl, Terima kasih atas posting Anda yang hebat Dan saya menguji opsi, android: largeHeap = "true" pada Galaxy S3 dengan JellyBean. Inilah hasilnya. [maxMemory: 256.0MB, memoryClass: 64MB] (maxMemory adalah 64MB tanpa opsi bigheap)
cmcromance
1
@Carl Haha Ini suatu kehormatan besar! :)
cmcromance
1
Untuk HTC One X: maxMemory: 67108864 memoryClass: 64
Kishore
1
LG Nexus 5 (4.4.3) (normal): maxMemory: 201326592, memoryClass: 192 | LG Nexus 5 (4.4.3) (tumpukan besar): maxMemory: 536870912, memoryClass: 192
ian.shaun.thomas
1
Sangat indah didokumentasikan. Harap berikan ini ke android. Saya tidak dapat menemukan dokumen Android yang sesuai
Jimit Patel
20

API resmi adalah:

Ini diperkenalkan pada 2.0 di mana perangkat memori yang lebih besar muncul. Anda dapat mengasumsikan bahwa perangkat yang menjalankan versi OS sebelumnya menggunakan kelas memori asli (16).

hackbod
sumber
13

Debug.getNativeHeapSize()akan melakukan trik, saya harus berpikir. Itu sudah ada sejak 1.0.

The Debugkelas memiliki banyak metode yang bagus untuk melacak alokasi dan kekhawatiran kinerja lainnya. Juga, jika Anda perlu mendeteksi situasi dengan memori rendah, periksa Activity.onLowMemory().

Neil Traft
sumber
Terima kasih, Neil. Apakah ini berfungsi saat aplikasi berjalan dengan debugging dimatikan?
hpique
2
Saya cukup baru dalam hal ini, tetapi saya tidak berpikir bahwa tumpukan asli sama dengan tumpukan aplikasi (Dalvik) yang merujuk pertanyaan. Tumpukan asli menyediakan dukungan untuk data bitmap, yang dialokasikan oleh kode asli, sementara tumpukan aplikasi menampung data aplikasi Java. Saya tertarik untuk mengetahui bahwa tumpukan asli dihitung terhadap batas tumpukan aplikasi, dan setelah 3,0 alokasi sebenarnya terjadi pada tumpukan aplikasi. Diane Hackborn (hackbod) memposting topik ini di sini: stackoverflow.com/questions/1945142/bitmaps-in-android Tetapi bahkan untuk 3.0, ada juga data non-"asli" pada tumpukan aplikasi.
Carl
1
Karena beberapa pembaruan Android terbaru (mungkin KitKat 4.4), bitmap berada di tumpukan JVM.
akauppi
13

Begini cara melakukannya:

Mendapatkan ukuran tumpukan maksimum yang dapat digunakan aplikasi:

Runtime runtime = Runtime.getRuntime();
long maxMemory=runtime.maxMemory();

Mendapatkan jumlah tumpukan yang digunakan aplikasi Anda saat ini:

long usedMemory=runtime.totalMemory() - runtime.freeMemory();

Mendapatkan seberapa banyak tumpukan yang kini dapat digunakan aplikasi Anda (memori yang tersedia):

long availableMemory=maxMemory-usedMemory;

Dan, untuk memformat masing-masing dengan baik, Anda dapat menggunakan:

String formattedMemorySize=Formatter.formatShortFileSize(context,memorySize); 
pengembang android
sumber
5

Ini mengembalikan ukuran tumpukan maksimum dalam byte:

Runtime.getRuntime().maxMemory()

Saya menggunakan ActivityManager.getMemoryClass () tetapi pada CyanogenMod 7 (saya tidak mengujinya di tempat lain) mengembalikan nilai yang salah jika pengguna menetapkan ukuran tumpukan secara manual.

fhucho
sumber
Karena doc untuk getMemoryClasstampaknya menyiratkan bahwa jumlahnya mungkin tidak sama dengan ukuran heap yang tersedia untuk vm Anda, dan karena doc untuk getNativeHeapSize... pendiam, saya benar-benar berpikir Runtime.getRuntime().maxMemory()adalah jawaban terbaik.
BoD
3

Beberapa operasi lebih cepat daripada pengelola ruang java heap. Menunda pengoperasian untuk beberapa waktu dapat membebaskan ruang memori. Anda dapat menggunakan metode ini untuk menghindari kesalahan ukuran tumpukan:

waitForGarbageCollector(new Runnable() {
  @Override
  public void run() {

    // Your operations.
  }
});

/**
 * Measure used memory and give garbage collector time to free up some
 * space.
 *
 * @param callback Callback operations to be done when memory is free.
 */
public static void waitForGarbageCollector(final Runnable callback) {

  Runtime runtime;
  long maxMemory;
  long usedMemory;
  double availableMemoryPercentage = 1.0;
  final double MIN_AVAILABLE_MEMORY_PERCENTAGE = 0.1;
  final int DELAY_TIME = 5 * 1000;

  runtime =
    Runtime.getRuntime();

  maxMemory =
    runtime.maxMemory();

  usedMemory =
    runtime.totalMemory() -
    runtime.freeMemory();

  availableMemoryPercentage =
    1 -
    (double) usedMemory /
    maxMemory;

  if (availableMemoryPercentage < MIN_AVAILABLE_MEMORY_PERCENTAGE) {

    try {
      Thread.sleep(DELAY_TIME);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    waitForGarbageCollector(
      callback);
  } else {

    // Memory resources are availavle, go to next operation:

    callback.run();
  }
}
Zon
sumber
diuji Bagus. Bisakah Anda menguraikan dua hal ini: availableMemoryPercentage dan MIN_AVAILABLE_MEMORY_PERCENTAGE? beberapa Penjelasan?
Noor Hossain
1
AvailableMemoryPercentagesesuai dengan rumus: berapa banyak memori perangkat saat ini bebas. MIN_AVAILABLE_MEMORY_PERCENTAGEadalah parameter khusus Anda, ambang di mana Anda mulai menunggu pengumpul sampah untuk melakukan tugasnya.
Zon
1

Asus Nexus 7 (2013) 32Gig: getMemoryClass () = 192 maxMemory () = 201326592

Saya membuat kesalahan dengan membuat prototip permainan saya di Nexus 7, dan kemudian menemukan kehabisan memori segera di tablet 4.04 generik istri saya (memoryclass 48, maxmemory 50331648)

Saya perlu merestrukturisasi proyek saya untuk memuat lebih sedikit sumber daya ketika saya menentukan memoryclass rendah.
Apakah ada cara di Jawa untuk melihat ukuran tumpukan saat ini? (Saya bisa melihatnya dengan jelas di logCat ketika debugging, tapi saya ingin cara melihatnya dalam kode untuk beradaptasi, seperti jika currentheap> (maxmemory / 2) membongkar bitmap berkualitas tinggi memuat kualitas rendah

Tkraindesigns
sumber
Pada kata Nexus7-2013, getLargeMemoryClass () = 512.
akauppi
0

Apakah maksud Anda secara terprogram, atau hanya ketika Anda sedang mengembangkan dan debugging? Jika yang terakhir, Anda bisa melihat info itu dari perspektif DDMS di Eclipse. Ketika emulator Anda (mungkin bahkan telepon fisik yang dicolokkan) sedang berjalan, itu akan mencantumkan proses aktif di jendela di sebelah kiri. Anda dapat memilihnya dan ada opsi untuk melacak alokasi heap.

Steve Haley
sumber
Maksudku secara terprogram. Klarifikasi pertanyaannya. Terima kasih.
hpique
1
Saya sarankan Anda melihat posting ini oleh salah satu programmer platform Android kemudian: stackoverflow.com/questions/2298208/…
Steve Haley
0
Runtime rt = Runtime.getRuntime();
rt.maxMemory()

nilainya b

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
am.getMemoryClass()

nilainya adalah MB

qinqie
sumber
Jenis apa rt? Di mana dinyatakan?
FindOut_Quran