如何以编程方式找到我的Android应用程序上使用的内存?
我希望有一种方法可以做到。另外,我也如何获得手机的空闲内存?
如何以编程方式找到我的Android应用程序上使用的内存?
我希望有一种方法可以做到。另外,我也如何获得手机的空闲内存?
Answers:
请注意,现代操作系统(例如Linux)上的内存使用是一个极其复杂且难以理解的领域。实际上,您正确解释任何数字的机会非常低。(几乎每当我与其他工程师一起查看内存使用量数字时,对于它们的实际含义总会进行长时间的讨论,这只会导致模糊的结论。)
注意:我们现在有更多有关管理应用程序内存的文档,其中涵盖了许多内容,并且是有关Android状态的最新信息。
首先可能要阅读本文的最后一部分,该部分讨论了如何在Android上管理内存:
现在ActivityManager.getMemoryInfo()
是我们用于查看整体内存使用情况的最高级别的API。这主要是为了帮助应用程序评估系统将近有多大空间,不再为后台进程提供更多的内存,因此需要开始杀死所需的进程(例如服务)。对于纯Java应用程序,这应该没什么用,因为Java堆限制的存在部分是为了避免一个应用程序能够在此时向系统施加压力。
进入较低级别,您可以使用Debug API获取有关内存使用情况的原始内核级别信息:android.os.Debug.MemoryInfo
请注意,从2.0开始,还有一个API ActivityManager.getProcessMemoryInfo
来获取有关另一个进程的此信息:ActivityManager.getProcessMemoryInfo(int [])
这将返回具有所有以下数据的低级MemoryInfo结构:
/** 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;
但是关于,,和... 之间的区别是什么Pss
,现在好了。PrivateDirty
SharedDirty
实际上,Android(通常是Linux系统)中的许多内存在多个进程之间共享。因此,一个进程使用多少内存确实不清楚。在将页面调度到磁盘的基础上再加上磁盘(更不用说我们在Android上不使用的交换)了,这甚至还不清楚。
因此,如果您要获取实际映射到每个进程中的所有物理RAM,并将所有进程加起来,则最终结果可能要比实际总RAM大得多。
该Pss
数字是内核计算的考虑内存共享的度量标准-基本上,一个进程中RAM的每个页面都是通过也使用该页面的其他进程数的比例来缩放的。这样,您可以(理论上)将所有进程的pss相加,以查看它们正在使用的总RAM,并比较各个进程之间的pss,以大致了解它们的相对权重。
另一个有趣的指标是 PrivateDirty
,它基本上是进程内无法分页到磁盘(不由磁盘上的相同数据支持)并且不与任何其他进程共享的RAM量。另一种看待这种情况的方式是,当该进程消失时,RAM将可供系统使用(并且可能很快被包含在缓存和其他用途中)。
差不多就是这个的SDK API。但是,作为设备开发人员,您可以做更多的事情。
使用adb
,您可以获得许多有关正在运行的系统的内存使用情况的信息。一个常见的命令adb shell dumpsys meminfo
是吐出有关每个Java进程的内存使用情况的一堆信息,其中包含上述信息以及其他各种信息。您还可以添加单个进程的名称或pid来查看,例如,adb shell dumpsys meminfo system
给我系统进程:
** pid 890 [系统]中的MEMINFO ** 达尔维克人其他总计 尺寸:10940 7047 N / A 17987 已分配:8943 5516 N / A 14459 免费:336 1531不适用1867 (Pss):4585 9282 11916 25783 (共享共享):2184 3596 916 6696 (私人肮脏):4504 5956 7456 17916 对象 浏览次数:149查看根数:4 AppContexts:13活动:0 资产:4 AssetManagers:4 本地绑定器:141代理绑定器:158 死亡人数:49 OpenSSL套接字:0 的SQL 堆:205 db文件:0 numPagers:0 inactivePageKB:0 activePageKB:0
顶部是主要部分,其中size
是特定堆的地址空间中的总大小,allocated
是堆认为具有的实际分配的kb,free
是堆可用于其他分配的剩余kb,并且pss
与priv dirty
相同如前所述,专门针对与每个堆相关的页面。
如果只想查看所有进程的内存使用情况,则可以使用命令adb shell procrank
。在同一系统上的输出如下所示:
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合子 977 24100K 24096K 5667K 4340K android.process.acore ... 59 336K 332K 99K 92K / system / bin /已安装 60 396K 392K 93K 84K / system / bin / keystore 51 280K 276K 74K 68K / system / bin / servicemanager 54 256K 252K 69K 64K / system / bin / debuggerd
这里的Vss
和Rss
列基本上是噪音(这些是进程的直接地址空间和RAM使用情况,如果将各个进程之间的RAM使用情况相加,则会得到非常大的数字)。
Pss
正如我们之前所见,并且Uss
是Priv Dirty
。
在此需要注意的有趣事情:Pss
与Uss
我们在中看到的内容略有不同(或略有不同)meminfo
。这是为什么?好procrank使用的内核机制与收集数据的机制不同meminfo
,它们给出的结果略有不同。这是为什么?老实说我没头绪。我相信procrank
可能是更准确的一个……但是,实际上,这只是要点:“获取一粒盐所得到的任何存储信息;通常是非常大的一粒。”
最后,该命令adb shell cat /proc/meminfo
提供了系统整体内存使用情况的摘要。这里有很多数据,只有前几个值得讨论(剩下的几个人很少理解,而我对这几个人的问题经常会引起相互矛盾的解释):
内存总量:395144 kB 内存免费:184936 kB 缓冲区:880 kB 缓存的:84104 kB 交换快取:0 kB
MemTotal
是内核和用户空间可用的内存总量(通常少于设备的实际物理RAM,因为无线电,DMA缓冲区等需要某些RAM)。
MemFree
是根本不使用的RAM数量。您在这里看到的数字非常高;通常在Android系统上只有几MB,因为我们尝试使用可用内存来保持进程运行
Cached
是用于文件系统缓存和其他此类事物的RAM。典型的系统将需要大约20MB的内存,以避免进入不良的分页状态。针对特定系统对Android内存不足杀手进行了调整,以确保在后台进程消耗过多的缓存RAM导致后台分页之前,杀死后台进程。
是的,您可以通过编程方式获取内存信息,并决定是否进行内存密集型工作。
通过调用获取VM堆大小:
Runtime.getRuntime().totalMemory();
通过调用获取已分配的VM内存:
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
通过调用获取VM堆大小限制:
Runtime.getRuntime().maxMemory()
通过调用以下命令获取本机分配的内存:
Debug.getNativeHeapAllocatedSize();
我制作了一个应用程序,以了解OutOfMemoryError行为并监视内存使用情况。
https://play.google.com/store/apps/details?id=net.coocood.oomresearch
您可以在https://github.com/coocood/oom-research上获取源代码。
这是一项正在进行的工作,但这是我不了解的内容:
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为什么未映射到activityManager.getProcessMemoryInfo()中的结果?显然,您想使结果数据有意义,那么Google为什么使关联结果如此困难?如果我要处理整个内存使用情况,当前系统甚至无法正常运行,因为返回的结果是android.os.Debug.MemoryInfo对象的数组,但是这些对象实际上都没有告诉您它们与哪些pid相关联。如果仅传递所有pid的数组,则将无法理解结果。据我了解,它的使用使一次传递多个pid变得毫无意义,然后,如果是这种情况,为什么要这样做,以便activityManager.getProcessMemoryInfo()仅采用一个int数组?
Hackbod的解决方案是Stack Overflow的最佳答案之一。它为一个非常模糊的主题提供了亮点。这对我帮助很大。
另一个非常有用的资源是以下必看视频:Google I / O 2011:Android应用程序的内存管理
更新:
Process Stats,一项用于发现您的应用程序如何管理内存的服务,在博客文章Process Stats:了解您的应用程序如何使用RAM中进行了解释的作者Dianne Hackborn:
Android Studio 0.8.10+引入了一个非常有用的工具,称为Memory Monitor。
它的优点是:
- 在图中显示可用内存和已用内存,以及随时间推移的垃圾回收事件。
- 快速测试应用程序运行缓慢是否可能与过多的垃圾回收事件有关。
- 快速测试应用崩溃是否可能与内存不足有关。
图1.在Android Memory Monitor上强制发生GC(垃圾收集)事件
通过使用它,您可以掌握有关应用程序RAM实时消耗的大量有用信息。
1)我想不是,至少不是来自Java。
2)
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
MemoryInfo mi = new MemoryInfo();
activityManager.getMemoryInfo(mi);
Log.i("memory free", "" + mi.availMem);
我们发现,获取当前进程总内存的所有标准方法都存在一些问题。
Runtime.getRuntime().totalMemory()
:仅返回JVM内存ActivityManager.getMemoryInfo()
,Process.getFreeMemory()
以及任何其他基于/proc/meminfo
-返回有关所有组合进程的内存信息(例如android_util_Process.cpp)Debug.getNativeHeapAllocatedSize()
-仅使用mallinfo()
其中返回有关由malloc()
函数和相关函数执行的内存分配的信息(请参阅android_os_Debug.cpp)Debug.getMemoryInfo()
-做得很好,但是太慢了。在Nexus 6上一次通话大约需要200毫秒。性能开销使该函数对我们无用,因为我们会定期调用它,并且每次调用都非常引人注意(请参阅android_os_Debug.cpp)ActivityManager.getProcessMemoryInfo(int[])
- Debug.getMemoryInfo()
内部调用(请参阅ActivityManagerService.java)最后,我们最终使用了以下代码:
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指标。您可以在此处找到有关它的更多详细信息:一,二和三。
PS我注意到,该主题仍然缺少实际和简单的代码段,这些代码段在性能不是关键要求的情况下如何估计进程的私有内存使用情况:
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中,他们引入了android-profiler来帮助您了解应用程序如何使用CPU,内存,网络和电池资源。
https://developer.android.com/studio/profile/android-profiler
有很多回答上述这肯定会帮助你,但(后买得起和研究亚洲开发银行存储工具2天)我想我可以用我的帮助的意见了。
正如 Hackbod所说:因此,如果您要获取实际映射到每个进程中的所有物理RAM,并将所有进程加起来,则最终结果可能要比实际总RAM大得多。 因此无法获得每个进程的确切内存量。
但是您可以通过某种逻辑接近它。我将告诉您如何。
还有像一些API
android.os.Debug.MemoryInfo
和ActivityManager.getMemoryInfo()
上面提到你已经有可能被了解和使用,但我会再谈其他方式
因此,首先您需要成为root用户才能使其正常运行。通过执行su
进程并以root特权进入控制台output and input stream
。然后在输出流中传递id\n
(输入)并将其写入过程输出,如果将获得包含的输入流uid=0
,则您是root用户。
现在这是您将在上述过程中使用的逻辑
当您获得进程传递的输出流时,使用命令(procrank,dumpsys meminfo等...)\n
而不是id并获取其inputstream
并读取,将流存储在bytes [],char []等中。使用raw数据完成!!!!!
许可:
<uses-permission android:name="android.permission.FACTORY_TEST"/>
检查您是否是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
}
}
使用以下命令执行命令 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();
}
}
您可以从控制台以单个字符串获取原始数据,而不是在任何情况下从任何API获取原始数据,由于原始数据需要手动分离,因此存储起来很复杂。
这只是一个尝试,如果我错过了什么,请建议我