Android uygulamamda kullanılan belleği programlı olarak nasıl bulabilirim?
Umarım bunu yapmanın bir yolu vardır. Ayrıca, telefonun boş hafızasını da nasıl alabilirim?
Android uygulamamda kullanılan belleği programlı olarak nasıl bulabilirim?
Umarım bunu yapmanın bir yolu vardır. Ayrıca, telefonun boş hafızasını da nasıl alabilirim?
Yanıtlar:
Linux gibi modern işletim sistemlerinde bellek kullanımının son derece karmaşık ve anlaşılması güç bir alan olduğunu unutmayın. Aslında aldığınız sayıları doğru yorumlama şansınız son derece düşüktür. (Diğer mühendislerle bellek kullanım numaralarına her baktığımda, aslında ne anlama geldikleri hakkında sadece belirsiz bir sonuca yol açtığı konusunda uzun bir tartışma var.)
Not: Artık, uygulamanızın Belleğini Yönetmeyle ilgili, burada malzemelerin çoğunu kapsayan ve Android'in durumu ile daha güncel olan çok daha kapsamlı belgelere sahibiz .
İlk şey, muhtemelen bu makalenin Android'de hafızanın nasıl yönetildiği hakkında bazı tartışmalara sahip olan son bölümünü okumaktır:
Android 2.0 ile başlayan Hizmet API'sı değişiklikleri
Şimdi ActivityManager.getMemoryInfo()
, genel bellek kullanımına bakmak için en üst düzey API'miz. Bu, çoğunlukla uygulamanın arka plan işlemleri için daha fazla belleğe sahip olmaya ne kadar yakın olduğunu ölçmesine yardımcı olmak için vardır, bu nedenle hizmetler gibi gerekli işlemleri öldürmeye başlaması gerekir. Saf Java uygulamaları için, bunun bir faydası yoktur, çünkü Java yığın sınırı, bir uygulamanın sistemi bu noktaya kadar strese sokmasını önlemek için kısmen vardır.
Daha alt seviyeye geçerek, bellek kullanımı hakkında ham çekirdek düzeyinde bilgi almak için Hata Ayıklama API'sını kullanabilirsiniz: android.os.Debug.MemoryInfo
2.0'dan başlayarak, ActivityManager.getProcessMemoryInfo
başka bir işlem hakkında bu bilgileri almak için bir API olduğunu unutmayın : ActivityManager.getProcessMemoryInfo (int [])
Bu, tüm bu verileri içeren düşük düzeyli bir MemoryInfo yapısı döndürür:
/** 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;
Ama fark arasındaki farkın ne kadar olduğu Pss
, PrivateDirty
ve SharedDirty
... iyi şimdi eğlence başlar.
Android'de (ve genel olarak Linux sistemlerinde) çok fazla bellek aslında birden çok süreçte paylaşılıyor. Yani bir işlemin ne kadar bellek kullandığı net değil. Disk üzerinde disk belleği (Android'de kullanmadığımız takas olsun) üzerine ekleyin ve daha az nettir.
Böylece, aslında her bir işlem için eşlenen tüm fiziksel RAM'i alıp tüm işlemleri toplarsanız, gerçek toplam RAM'den çok daha büyük bir sayı elde edersiniz.
Pss
Ayrıca o sayfayı kullanan diğer işlemlerin sayısının bir oranı olarak ölçeklendirilmiş bir süreçte RAM temelde her sayfa - sayı hesabı bellek paylaşımı dikkate alır çekirdek değerlerini hesaplar bir ölçümdür. Bu şekilde (teoride) kullandıkları toplam RAM'i görmek için tüm süreçler arasında pss'i ekleyebilir ve göreceli ağırlıkları hakkında kaba bir fikir edinmek için süreçler arasındaki pss'yi karşılaştırabilirsiniz.
Buradaki diğer ilginç metrik PrivateDirty
, temel olarak, işlemin içindeki diske sayfalanamayan (diskteki aynı verilerle desteklenmeyen) RAM miktarıdır ve başka herhangi bir işlemle paylaşılmaz. Buna bakmanın bir başka yolu, bu işlem kaybolduğunda (ve muhtemelen hızlı bir şekilde önbelleklere ve diğer kullanımlarına maruz kaldığında) sistem tarafından kullanılabilecek olan RAM'dir.
Bu hemen hemen bunun için SDK API'leri. Ancak, cihazınızla bir geliştirici olarak yapabileceğiniz daha çok şey var.
Kullanarak adb
, çalışan bir sistemin bellek kullanımı hakkında alabileceğiniz birçok bilgi vardır. Yaygın olanı, adb shell dumpsys meminfo
her bir Java işleminin bellek kullanımı hakkında, yukarıdaki bilgileri ve çeşitli diğer şeyleri içeren bir grup bilgi verecek olan komuttur . Ayrıca görmek için tek bir işlemin adını veya pid'ini de kullanabilirsiniz, örneğin adb shell dumpsys meminfo system
bana sistem işlemini verin:
** pid 890 [sistem] içinde MEMINFO ** yerli dalvik diğer toplam boyut: 10940 7047 Yok 17987 tahsis edilen: 8943 5516 YOK 14459 ücretsiz: 336 1531 Yok 1867 (Pss): 4585 9282 11916 25783 (paylaşılan kirli): 2184 3596 916 6696 (özel kirli): 4504 5956 7456 17916 Nesneler İzlenme: 149 İzleme AppContexts: 13 Etkinlikler: 0 Varlıklar: 4 Varlık Yerel Bağlayıcılar: 141 Proxy Bağlayıcıları: 158 Ölüm Alıcıları: 49 OpenSSL Soketleri: 0 SQL yığın: 205 db numPagers: 0 etkin değilPageKB: 0 activePageKB: 0
Üst bölüm, ana biridir size
, belirli bir yığın bir adres alanı içindeki toplam boyutudur allocated
, yığın sahip olduğu düşündüğü gerçek tahsislerinin kb free
kalan kb yığın ek ayırma işlemleri için olan serbest ve pss
ve priv dirty
aynı daha önce açıklandığı gibi yığınların her biriyle ilişkilendirilmiş sayfalara özel.
Tüm işlemlerde bellek kullanımına bakmak istiyorsanız, komutu kullanabilirsiniz adb shell procrank
. Bunun aynı sistemde çıktısı şuna benzer:
PID Vss Rss Pss Uss cmdline 890 84456K 48668K 25850K 21284K sistem_sunucusu 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 zigot 977 24100K 24096K 5667K 4340K android.process.acore ... 59 336K 332K 99K 92K / sistem / çöp kutusu / kurulu 60396K 392K 93K 84K / sistem / depo gözü / anahtar deposu 51 280K 276K 74K 68K / sistem / depo gözü / servis cihazı 54256K 252K 69K 64K / sistem / depo gözü / hata ayıklayıcı
Burada Vss
ve Rss
sütunları temelde gürültülüdür (bunlar bir işlemin doğrudan adres alanı ve RAM kullanımıdır, burada RAM kullanımını süreçlere eklerseniz gülünç büyük bir sayı elde edersiniz).
Pss
daha önce gördüğümüz gibi ve Uss
öyle Priv Dirty
.
Burada dikkat edilmesi gereken ilginç bir şey: Pss
ve Uss
gördüğümüzden biraz (veya biraz daha fazla) farklı meminfo
. Neden? Procrank, verilerini toplamak için olduğundan farklı bir çekirdek mekanizması kullanır meminfo
ve biraz farklı sonuçlar verir. Neden? Dürüst olmak gerekirse bir ipucum yok. procrank
Daha doğru olabileceğine inanıyorum ... ama gerçekten, bu sadece noktayı terk ediyor: "bir tuz tanesi ile aldığınız herhangi bir hafıza bilgisini alın; genellikle çok büyük bir tane."
Son olarak adb shell cat /proc/meminfo
sistemin genel bellek kullanımının bir özetini veren komut vardır. Burada çok fazla veri var, sadece tartışmaya değer ilk birkaç sayı (ve az sayıda insan tarafından anlaşılan geri kalanlar ve bu birkaç insanla ilgili sorularım genellikle çelişkili açıklamalarla sonuçlanıyor):
MemToplam: 395144 kB MemFree: 184936 kB Tamponlar: 880 kB Önbellek: 84104 kB Önbellek: 0 kB
MemTotal
çekirdek ve kullanıcı alanı için kullanılabilir toplam bellek miktarıdır (genellikle cihazın gerçek fiziksel RAM'inden daha azdır, çünkü bu RAM'in bir kısmı radyo, DMA tamponları vb. için gereklidir).
MemFree
hiç kullanılmayan RAM miktarıdır. Burada gördüğünüz sayı çok yüksek; genellikle bir Android sisteminde bu sadece birkaç MB olacaktır, çünkü süreçleri çalışmaya devam etmek için kullanılabilir belleği kullanmaya çalışıyoruz
Cached
dosya sistemi önbellekleri ve benzeri şeyler için kullanılan RAM'dir. Kötü çağrı durumlarına girmekten kaçınmak için tipik sistemlerin 20 MB kadar olması gerekir; Android, bellek dışı katil, önbelleğe alınmış RAM, bu tür disk belleği ile sonuçlanacak şekilde çok fazla tüketilmeden önce arka plan işlemlerinin öldürüldüğünden emin olmak için belirli bir sistem için ayarlanmıştır.
Evet, bellek bilgilerini programlı olarak alabilir ve bellek yoğun çalışma yapıp yapmayacağınıza karar verebilirsiniz.
Arayarak VM Yığın Boyutu alın:
Runtime.getRuntime().totalMemory();
Şunları arayarak Tahsis Edilen VM Belleğini alın:
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
Arayarak VM Yığın Boyutu Sınırı alın:
Runtime.getRuntime().maxMemory()
Arayarak Yerel Tahsisli Belleği Alın:
Debug.getNativeHeapAllocatedSize();
OutOfMemoryError davranışını anlamak ve bellek kullanımını izlemek için bir uygulama yaptım.
https://play.google.com/store/apps/details?id=net.coocood.oomresearch
Kaynak kodunu https://github.com/coocood/oom-research adresinden alabilirsiniz.
Bu devam eden bir çalışma, ama anlamadığım şey bu:
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");
}
}
PID neden activityManager.getProcessMemoryInfo () öğesinde sonuçla eşlenmiyor? Açıkça ortaya çıkan verileri anlamlı hale getirmek istiyorsunuz, bu yüzden Google sonuçları ilişkilendirmeyi neden bu kadar zorlaştırdı? Döndürülen sonuç bir android.os.Debug.MemoryInfo nesneleri dizisi olduğu için tüm bellek kullanımını işlemek istersem mevcut sistem bile iyi çalışmıyor, ancak bu nesnelerin hiçbiri size hangi pidelerle ilişkili olduğunu söylemiyor. Tüm pidlerden oluşan bir diziyi geçerseniz, sonuçları anlamanız mümkün olmaz. Kullanımını anladığım kadarıyla, bir kerede birden fazla pid'i geçmeyi anlamsız hale getirir ve sonra durum buysa, neden eventManager.getProcessMemoryInfo () sadece bir int dizisi alır?
Hackbod's Stack Overflow'un en iyi cevaplarından biridir. Çok belirsiz bir konuya ışık tutuyor. Bana çok yardımcı oldu.
Gerçekten yararlı bir başka kaynak da görülmesi gereken video: Google I / O 2011: Android Uygulamaları için bellek yönetimi
GÜNCELLEME:
İşlem İstatistikleri, uygulamanızın blog yayınında açıklanan belleği nasıl yönettiğini keşfetmek için bir hizmet. İşlem İstatistikleri: Uygulamanızın Dianne Hackborn tarafından RAM'i Nasıl Kullantığını Anlama:
Android Studio 0.8.10+, Memory Monitor adlı inanılmaz kullanışlı bir araç tanıttı .
Ne için iyi:
- Bir grafikte kullanılabilir ve kullanılmış bellek ve zaman içinde çöp toplama olayları gösteriliyor.
- Uygulama yavaşlığının aşırı çöp toplama olaylarıyla ilişkili olup olmadığını hızla test edin.
- Uygulama kilitlenmelerinin belleğin tükenmesi ile ilgili olup olmadığını hızla test edin.
Şekil 1. Android Bellek İzleyicisi'nde bir GC (Çöp Toplama) olayını zorlama
Bunu kullanarak uygulamanızın RAM gerçek zamanlı tüketimi hakkında bol miktarda iyi bilgiye sahip olabilirsiniz.
1) Sanırım hayır, en azından Java'dan değil.
2)
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
MemoryInfo mi = new MemoryInfo();
activityManager.getMemoryInfo(mi);
Log.i("memory free", "" + mi.availMem);
Mevcut sürecin toplam belleğini almanın tüm standart yollarının bazı sorunları olduğunu öğrendik.
Runtime.getRuntime().totalMemory()
: yalnızca JVM belleği döndürürActivityManager.getMemoryInfo()
, Process.getFreeMemory()
Dayalı başka ve bir şey /proc/meminfo
- kombine tüm işlemler hakkında döner hafıza bilgileri (örneğin android_util_Process.cpp )Debug.getNativeHeapAllocatedSize()
- yalnızca ilgili işlevler mallinfo()
tarafından gerçekleştirilen bellek ayırmaları malloc()
ve ilgili işlevler hakkında bilgi veren kullanır (bkz. android_os_Debug.cpp )Debug.getMemoryInfo()
- işi yapıyor ama çok yavaş. Yaklaşık sürer 200ms üzerinde Nexus 6 Tek bir çağrı için. Performans yükü, düzenli olarak dediğimiz ve her çağrı oldukça dikkat çekici olduğu için bu işlevi bizim için işe yaramaz hale getirir (bkz. Android_os_Debug.cpp )ActivityManager.getProcessMemoryInfo(int[])
- Debug.getMemoryInfo()
dahili olarak arama yapar (bkz. ActivityManagerService.java )Son olarak, aşağıdaki kodu kullandık:
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;
VmRSS metriğini döndürür . Bununla ilgili daha fazla ayrıntıyı burada bulabilirsiniz: bir , iki ve üç .
PS: Temanın performans kritik bir gereklilik değilse, sürecin özel bellek kullanımını nasıl tahmin edeceğine dair gerçek ve basit bir kod snippet'inin eksik olduğunu fark ettim :
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;
Android Studio 3.0'da, uygulamanızın CPU, bellek, ağ ve pil kaynaklarını nasıl kullandığını anlamanıza yardımcı olmak için android-profiler tanıttılar.
https://developer.android.com/studio/profile/android-profiler
Yukarıda kesinlikle size yardımcı olacak bir sürü cevap var ama (adb bellek araçları üzerinde 2 gün göze ve araştırma sonra) Ben de benim görüşüme yardımcı olabilir düşünüyorum .
As Hackbod diyor ki: aslında her işlem için eşlenen fiziksel RAM hepsini almak ve süreçlerin tüm ekleyebilirsiniz edildi Böylece, muhtemelen gerçek toplam RAM daha sayı çok daha fazla ile bitirmek istiyorum. bu nedenle işlem başına tam bellek miktarını elde etmenin bir yolu yoktur.
Ama ona bir mantıkla yaklaşabilirsin ... ve nasıl olduğunu anlatacağım ..
Yukarıda halihazırda okunmuş ve kullanılmış olabilecek bazı API'ler var
android.os.Debug.MemoryInfo
veActivityManager.getMemoryInfo()
bahsettik, ancak başka bir yoldan konuşacağım
Bu yüzden öncelikle çalışabilmesi için kök kullanıcı olmanız gerekir. su
Süreçte çalışarak kök ayrıcalığına sahip konsol içine alın ve edinin output and input stream
. Sonra geçer id\n
(enter) ouputstream ve proses çıkış yazmak, varsa bir inputStream içeren alacak uid=0
, kök kullanıcı bulunmaktadır.
Şimdi yukarıdaki işlemde kullanacağınız mantık
Eğer işlem geçmek ouputstream\n
olsun id yerine komut (procrank, dumpsys meminfo vb ...) almak ve almak inputstream
ve okumak, akışı bayt [], char [] vb saklamak .. ham veri kullanın .. ve sen yapılır !!!!!
izin:
<uses-permission android:name="android.permission.FACTORY_TEST"/>
Kök kullanıcı olup olmadığınızı kontrol edin:
// 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
}
}
Komutunuzu 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();
}
}
El ile ayırmanız gerekeceğinden saklanması karmaşık olan herhangi bir API'dan, bazı durumlarda konsoldan tek bir dizede ham veri alırsınız .
Bu sadece bir deneme, lütfen bir şey kaçırırsam öner