How do I discover memory usage of my application in Android?
How can I find the memory used on my Android application, programmati开发者_如何学JAVAcally?
I hope there is a way to do it. Plus, how do I get the free memory of the phone too?
Note that memory usage on modern operating systems like Linux is an extremely complicated and difficult to understand area. In fact the chances of you actually correctly interpreting whatever numbers you get is extremely low. (Pretty much every time I look at memory usage numbers with other engineers, there is always a long discussion about what they actually mean that only results in a vague conclusion.)
Note: we now have much more extensive documentation on Managing Your App's Memory that covers much of the material here and is more up-to-date with the state of Android.
First thing is to probably read the last part of this article which has some discussion of how memory is managed on Android:
Service API changes starting with Android 2.0
Now ActivityManager.getMemoryInfo()
is our highest-level API for looking at overall memory usage. This is mostly there to help an application gauge how close the system is coming to having no more memory for background processes, thus needing to start killing needed processes like services. For pure Java applications, this should be of little use, since the Java heap limit is there in part to avoid one app from being able to stress the system to this point.
Going lower-level, you can use the Debug API to get raw kernel-level information about memory usage: android.os.Debug.MemoryInfo
Note starting with 2.0 there is also an API, ActivityManager.getProcessMemoryInfo
, to get this information about another process: ActivityManager.getProcessMemoryInfo(int[])
This returns a low-level MemoryInfo structure with all of this data:
/** 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;
But as to what the difference is between Pss
, PrivateDirty
, and SharedDirty
... well now the fun begins.
A lot of memory in Android (and Linux systems in general) is actually shared across multiple processes. So how much memory a processes uses is really not clear. Add on top of that paging out to disk (let alone swap which we don't use on Android) and it is even less clear.
Thus if you were to take all of the physical RAM actually mapped in to each process, and add up all of the processes, you would probably end up with a number much greater than the actual total RAM.
The Pss
number is a metric the kernel computes that takes into account memory sharing -- basically each page of RAM in a process is scaled by a ratio of the number of other processes also using that page. This way you can (in theory) add up the pss across all processes to see the total RAM they are using, and compare pss between processes to get a rough idea of their relative weight.
The other interesting metric here is PrivateDirty
, which is basically the amount of RAM inside the process that can not be paged to disk (it is not backed by the same data on disk), and is not shared with any other processes. Another way to look at this is the RAM that will become available to the system when that process goes away (and probably quickly subsumed into caches and other uses of it).
That is pretty much the SDK APIs for this. However there is more you can do as a developer with your device.
Using adb
, there is a lot of information you can get about the memory use of a running system. A common one is the command adb shell dumpsys meminfo
which will spit out a bunch of information about the memory use of each Java process, containing the above info as well as a variety of other things. You can also tack on the name or pid of a single process to see, for example adb shell dumpsys meminfo system
give me the system process:
** MEMINFO in pid 890 [system] ** native dalvik other total size: 10940 7047 N/A 17987 allocated: 8943 5516 N/A 14459 free: 336 1531 N/A 1867 (Pss): 4585 9282 11916 25783 (shared dirty): 2184 3596 916 6696 (priv dirty): 4504 5956 7456 17916 Objects Views: 149 ViewRoots: 4 AppContexts: 13 Activities: 0 Assets: 4 AssetManagers: 4 Local Binders: 141 Proxy Binders: 158 Death Recipients: 49 OpenSSL Sockets: 0 SQL heap: 205 dbFiles: 0 numPagers: 0 inactivePageKB: 0 activePageKB: 0
The top section is the main one, where size
is the total size in address space of a particular heap, allocated
is the kb of actual allocations that heap thinks it has, free
is the remaining kb free the heap has for additional allocations, and pss
and priv dirty
are the same as discussed before specific to pages associated with each of the heaps.
If you just want to look at memory usage across all processes, you can use the command adb shell procrank
. Output of this on the same system looks like:
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
Here the Vss
and Rss
columns are basically noise (these are the straight-forward address space and RAM usage of a process, where if you add up the RAM usage across processes you get an ridiculously large number).
Pss
is as we've seen before, and Uss
is Priv Dirty
.
Interesting thing to note here: Pss
and Uss
are slightly (or more than slightly) different than what we saw in meminfo
. Why is that? Well procrank uses a different kernel mechanism to collect its data than meminfo
does, and they give slightly different results. Why is that? Honestly I haven't a clue. I believe procrank
may be the more accurate one... but really, this just leave the point: "take any memory info you get with a grain of salt; often a very large grain."
Finally there is the command adb shell cat /proc/meminfo
that gives a summary of the overall memory usage of the system. There is a lot of data here, only the first few numbers worth discussing (and the remaining ones understood by few people, and my questions of those few people about them often resulting in conflicting explanations):
MemTotal: 395144 kB MemFree: 184936 kB Buffers: 880 kB Cached: 84104 kB SwapCached: 0 kB
MemTotal
is the total amount of memory available to the kernel and user space (often less than the actual physical RAM of the device, since some of that RAM is needed for the radio, DMA buffers, etc).
MemFree
is the amount of RAM that is not being used at all. The number you see here is very high; typically on an Android system this would be only a few MB, since we try to use available memory to keep processes running
Cached
is the RAM being used for filesystem caches and other such things. Typical systems will need to have 20MB or so for this to avoid getting into bad paging states; the Android out of memory killer is tuned for a particular system to make sure that background processes are killed before the cached RAM is consumed too much by them to result in such paging.
Yes, you can get memory info programmatically and decide whether to do memory intensive work.
Get VM Heap Size by calling:
Runtime.getRuntime().totalMemory();
Get Allocated VM Memory by calling:
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
Get VM Heap Size Limit by calling:
Runtime.getRuntime().maxMemory()
Get Native Allocated Memory by calling:
Debug.getNativeHeapAllocatedSize();
I made an app to figure out the OutOfMemoryError behavior and monitor memory usage.
https://play.google.com/store/apps/details?id=net.coocood.oomresearch
You can get the source code at https://github.com/coocood/oom-research
This is a work in progress, but this is what I don't understand:
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");
}
}
Why isn't the PID mapped to the result in activityManager.getProcessMemoryInfo()? Clearly you want to make the resulting data meaningful, so why has Google made it so difficult to correlate the results? The current system doesn't even work well if I want to process the entire memory usage since the returned result is an array of android.os.Debug.MemoryInfo objects, but none of those objects actually tell you what pids they are associated with. If you simply pass in an array of all pids, you will have no way to understand the results. As I understand it's use, it makes it meaningless to pass in more than one pid at a time, and then if that's the case, why make it so that activityManager.getProcessMemoryInfo() only takes an int array?
Hackbod's is one of the best answers on Stack Overflow. It throws light on a very obscure subject. It helped me a lot.
Another really helpful resource is this must-see video: Google I/O 2011: Memory management for Android Apps
UPDATE:
Process Stats, a service to discover how your app manages memory explained at the blog post Process Stats: Understanding How Your App Uses RAM by Dianne Hackborn:
Android Studio 0.8.10+ has introduced an incredibly useful tool called Memory Monitor.
What it's good for:
- Showing available and used memory in a graph, and garbage collection events over time.
- Quickly testing whether app slowness might be related to excessive garbage collection events.
- Quickly testing whether app crashes may be related to running out of memory.
Figure 1. Forcing a GC (Garbage Collection) event on Android Memory Monitor
You can have plenty good information on your app's RAM real-time consumption by using it.
1) I guess not, at least not from Java.
2)
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
MemoryInfo mi = new MemoryInfo();
activityManager.getMemoryInfo(mi);
Log.i("memory free", "" + mi.availMem);
We found out that all the standard ways of getting the total memory of the current process have some issues.
Runtime.getRuntime().totalMemory()
: returns JVM memory onlyActivityManager.getMemoryInfo()
,Process.getFreeMemory()
and anything else based on/proc/meminfo
- returns memory info about all the processes combined (e.g. android_util_Process.cpp)Debug.getNativeHeapAllocatedSize()
- usesmallinfo()
which return information about memory allocations performed bymalloc()
and related functions only (see android_os_Debug.cpp)Debug.getMemoryInfo()
- does the job but it's too slow. It takes about 200ms on Nexus 6 for a single call. The performance overhead makes this function useless for us as we call it regularly and every call is quite noticeable (see android_os_Debug.cpp)ActivityManager.getProcessMemoryInfo(int[])
- callsDebug.getMemoryInfo()
internally (see ActivityManagerService.java)
Finally, we ended up using the following code:
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;
It returns VmRSS metric. You can find more details about it here: one, two and three.
P.S. I noticed that the theme still has a lack of an actual and simple code snippet of how to estimate the private memory usage of the process if the performance isn't a critical requirement:
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;
In android studio 3.0 they have introduced android-profiler to help you to understand how your app uses CPU, memory, network, and battery resources.
https://developer.android.com/studio/profile/android-profiler
There are a lot of answer above which will definitely help you but (after 2 days of afford and research on adb memory tools) I think i can help with my opinion too.
As Hackbod says : Thus if you were to take all of the physical RAM actually mapped in to each process, and add up all of the processes, you would probably end up with a number much greater than the actual total RAM. so there is no way you can get exact amount of memory per process.
But you can get close to it by some logic..and I will tell how..
There are some API like
android.os.Debug.MemoryInfo
andActivityManager.getMemoryInfo()
mentioned above which you already might have being read about and used but I will talk about other way
So firstly you need to be a root user to get it work. Get into console with root privilege by executing su
in process and get its output and input stream
. Then pass id\n
(enter) in ouputstream and write it to process output, If will get an inputstream containing uid=0
, you are root user.
Now here is the logic which you will use in above process
When you get ouputstream of process pass you command (procrank, dumpsys meminfo etc...) with \n
instead of id and get its inputstream
and read, store the stream in bytes[ ] ,char[ ] etc.. use raw data..and you are done!!!!!
permission :
<uses-permission android:name="android.permission.FACTORY_TEST"/>
Check if you are root user :
// 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
}
}
Execute your command with 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 :
You get a raw data in a single string from console instead of in some instance from any API,which is complex to store as you will need to separate it manually.
This is just a try, please suggest me if I missed something
I got confused while reading answers, So I decided to read docs. OK, Let's do it:
Native heap memory usage
You can get native heap memory size of the device using Debug object:
long nativeTotal = Debug.getNativeHeapSize();
long nativeFree = Debug.getNativeHeapFreeSize();
long nativeAllocated = Debug.getNativeHeapAllocatedSize();
long nativeUsed = nativeTotal - nativeFree;
Note: nativeUsed
and nativeAllocated
have the same value.
Runtime memory usage
Memory usage using Runtime object of the application, Every application has a runtime object to access its interface:
Runtime runtime = Runtime.getRuntime();
long runtimeMax = runtime.maxMemory();
long runtimeTotal = runtime.totalMemory();
long runtimeFree = runtime.freeMemory();
long runtimeUsed = runtimeTotal - runtimeFree;
System memory usage
You can get the memory usage of the system using ActivityManager.MemoryInfo object:
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);
long systemTotal = memoryInfo.totalMem;
long systemFree = memoryInfo.availMem;
long systemUsed = systemTotal - systemFree;
Proc memory usage
Another way to read the memory usage of the system is by parsing the contents of the /proc/meminfo file in linux.
RandomAccessFile reader = new RandomAccessFile("/proc/meminfo", "r");
Pattern pattern = Pattern.compile("(\\d+)");
Matcher totalMatcher = pattern.matcher(reader.readLine());
totalMatcher.find();
long procTotal = Long.parseLong(totalMatcher.group(1)) * 1024L; // KB to B
Matcher freeMatcher = pattern.matcher(reader.readLine());
freeMatcher.find();
long procFree = Long.parseLong(freeMatcher.group(1)) * 1024L; // KB to B
long procUsed = procTotal - procFree;
OK, I am still confused. But this was all the ways you can get the memory usage. LOG THEM ALL TO DEBUG YOUR APPLICATION!!!
I also provided a link for each of them to read their usage and information.
精彩评论