这里简单总结下Android获取内存的方式,方式仅限于系统开放给应用层的API,adb命令比较简单,不在本题讨论范围内,想了解的可以参考之前文章:性能优化工具(十)- Android内存分析命令。
一、AMS获取内存信息
1.1 获取方式
ActivityManager am = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
am.getMemoryInfo(memoryInfo);
memoryInfo.totalMem//系统总内存
memoryInfo.availMem//可使用内存
1.2 源码分析
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
final long homeAppMem = mProcessList.getMemLevel(ProcessList.HOME_APP_ADJ);
final long cachedAppMem = mProcessList.getMemLevel(ProcessList.CACHED_APP_MIN_ADJ);
outInfo.availMem = getFreeMemory();
outInfo.totalMem = getTotalMemory();
outInfo.threshold = homeAppMem;
outInfo.lowMemory = outInfo.availMem < (homeAppMem + ((cachedAppMem-homeAppMem)/2));
outInfo.hiddenAppThreshold = cachedAppMem;
outInfo.secondaryServerThreshold = mProcessList.getMemLevel(
ProcessList.SERVICE_ADJ);
outInfo.visibleAppThreshold = mProcessList.getMemLevel(
ProcessList.VISIBLE_APP_ADJ);
outInfo.foregroundAppThreshold = mProcessList.getMemLevel(
ProcessList.FOREGROUND_APP_ADJ);
}
getFreeMemory与getTotalMemory都是android.os.Process的方法:
android/os/Process.java
public static final native long getFreeMemory();
public static final native long getTotalMemory();
native方法具体实现:
frameworks/base/core/jni/android_util_Process.cpp
static jlong android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
{
static const char* const sums[] = { "MemFree:", "Cached:", NULL };
static const size_t sumsLen[] = { strlen("MemFree:"), strlen("Cached:"), 0 };
return getFreeMemoryImpl(sums, sumsLen, 2);
}
static jlong android_os_Process_getTotalMemory(JNIEnv* env, jobject clazz)
{
static const char* const sums[] = { "MemTotal:", NULL };
static const size_t sumsLen[] = { strlen("MemTotal:"), 0 };
return getFreeMemoryImpl(sums, sumsLen, 1);
}
static jlong getFreeMemoryImpl(const char* const sums[], const size_t sumsLen[], size_t num)
{
int fd = open("/proc/meminfo", O_RDONLY);
if (fd < 0) {
ALOGW("Unable to open /proc/meminfo");
return -1;
}
char buffer[256];
const int len = read(fd, buffer, sizeof(buffer)-1);
close(fd);
if (len < 0) {
ALOGW("Unable to read /proc/meminfo");
return -1;
}
buffer[len] = 0;
size_t numFound = 0;
jlong mem = 0;
char* p = buffer;
while (*p && numFound < num) {
int i = 0;
while (sums[i]) {
if (strncmp(p, sums[i], sumsLen[i]) == 0) {
p += sumsLen[i];
while (*p == ' ') p++;
char* num = p;
while (*p >= '0' && *p <= '9') p++;
if (*p != 0) {
*p = 0;
p++;
if (*p == 0) p--;
}
mem += atoll(num) * 1024;
numFound++;
break;
}
i++;
}
p++;
}
return numFound > 0 ? mem : -1;
}
该方式是从/proc/meminfo文件读取内存信息:
adb shell cat proc/meminfo
MemTotal: 3721316 kB
MemFree: 132652 kB
MemAvailable: 1250484 kB
Buffers: 87284 kB
Cached: 1205132 kB
…
1.3 总结
memoryInfo.totalMem = MemTotal
memoryInfo.availMem = Cached+MemFree
二、Runtime获取内存信息
2.1 获取方式
Runtime runtime = Runtime.getRuntime();
runtime.maxMemory()
runtime.totalMemory()
runtime.freeMemory()
2.2源码分析
libcore/ojluni/src/main/java/java/lang/Runtime.java
@FastNative
public native long freeMemory();
@FastNative
public native long totalMemory();
@FastNative
public native long maxMemory();
看看具体实现:
libcore/ojluni/src/main/native/Runtime.c
JNIEXPORT jlong JNICALL
Runtime_freeMemory(JNIEnv *env, jobject this)
{
return JVM_FreeMemory();
}
JNIEXPORT jlong JNICALL
Runtime_totalMemory(JNIEnv *env, jobject this)
{
return JVM_TotalMemory();
}
JNIEXPORT jlong JNICALL
Runtime_maxMemory(JNIEnv *env, jobject this)
{
return JVM_MaxMemory();
}
art/runtime/openjdkjvm/OpenjdkJvm.cc
JNIEXPORT jlong JVM_FreeMemory(void) {
return art::Runtime::Current()->GetHeap()->GetFreeMemory();
}
JNIEXPORT jlong JVM_TotalMemory(void) {
return art::Runtime::Current()->GetHeap()->GetTotalMemory();
}
JNIEXPORT jlong JVM_MaxMemory(void) {
return art::Runtime::Current()->GetHeap()->GetMaxMemory();
}
以GetTotalMemory为例,层层来找:
art/runtime/runtime.h
namespace art {
class Runtime {
...
static Runtime* Current() { //runtime实例
return instance_;
}
…
gc::Heap* GetHeap() const {
return heap_;
}
}
}
这里执行的是Heap的GetTotalMemory方法,找下Heap
art/runtime/gc/heap.h
// When the number of bytes allocated exceeds the footprint TryAllocate returns null indicating
// a GC should be triggered.
size_t max_allowed_footprint_;
// Returns the number of bytes currently allocated.
size_t GetBytesAllocated() const {
return num_bytes_allocated_.LoadSequentiallyConsistent();
}
art/runtime/gc/heap.cc
size_t Heap::GetTotalMemory() const {
//max_allowed_footprint_:当前阶段最大已申请的内存大小
//GetBytesAllocated():当前已分配内存大小
return std::max(max_allowed_footprint_, GetBytesAllocated());
}
totalMemory也就是指当前虚拟机进程已经向系统申请的内存大小。
art/runtime/gc/heap.h
// Returns the number of bytes currently allocated.
size_t GetBytesAllocated() const {
return num_bytes_allocated_.LoadSequentiallyConsistent();//当前分配内存大小
}
// Implements java.lang.Runtime.maxMemory, returning the maximum amount of memory a program can
// consume. For a regular VM this would relate to the -Xmx option and would return -1 if no Xmx
// were specified. Android apps start with a growth limit (small heap size) which is
// cleared/extended for large apps.
size_t GetMaxMemory() const {
// There is some race conditions in the allocation code that can cause bytes allocated to
// become larger than growth_limit_ in rare cases.
//当前已分配内存 与 增长上限的最大值。其实也就是虚拟机最大可向系统申请到的内存大小
return std::max(GetBytesAllocated(), growth_limit_);
}
// Returns how much free memory we have until we need to grow the heap to perform an allocation.
// Similar to GetFreeMemoryUntilGC. Implements java.lang.Runtime.freeMemory.
size_t GetFreeMemory() const {
size_t byte_allocated = num_bytes_allocated_.LoadSequentiallyConsistent();
size_t total_memory = GetTotalMemory();
// Make sure we don't get a negative number.
//freeMemory = totalMemory - 已分配的内存
return total_memory - std::min(total_memory, byte_allocated);
}
2.3 总结
runtime.maxMemory()//获取虚拟机最大分配内存
runtime.totalMemory()//获取虚拟机当前申请到的内存大小
runtime.freeMemory()//获取当前申请内存的剩余内存大小
距离oom还剩余的可分配内存空间:
runtime.maxMemory() - (runtime.totalMemory() - runtime.freeMemory())
三、Debug获取内存信息
3.1获取方式
Debug.MemoryInfo mi = new Debug.MemoryInfo();
Debug.getMemoryInfo(mi);
mi.getTotalPss()
mi.getMemoryStat("summary.java-heap")
mi.dalvikPss
mi.nativePss
…
Debug.getNativeHeapSize()
Debug.getNativeHeapFreeSize()
…
Debug获取内存的api比较多。
3.2源码分析
android/os/Debug.java
/**
* Retrieves information about this processes memory usages. This information is broken down by
* how much is in use by dalvik, the native heap, and everything else.
*
* <p><b>Note:</b> this method directly retrieves memory information for the given process
* from low-level data available to it. It may not be able to retrieve information about
* some protected allocations, such as graphics. If you want to be sure you can see
* all information about allocations by the process, use
* {@link android.app.ActivityManager#getProcessMemoryInfo(int[])} instead.</p>
*/
public static native void getMemoryInfo(MemoryInfo memoryInfo);
/**
* Returns the size of the native heap.
* @return The size of the native heap in bytes.
*/
public static native long getNativeHeapSize();
/**
* Returns the amount of allocated memory in the native heap.
* @return The allocated size in bytes.
*/
public static native long getNativeHeapAllocatedSize();
/**
* Returns the amount of free memory in the native heap.
* @return The freed size in bytes.
*/
public static native long getNativeHeapFreeSize();
frameworks/base/core/jni/android_os_Debug.cpp
static const JNINativeMethod gMethods[] = {
{ "getNativeHeapSize", "()J",
(void*) android_os_Debug_getNativeHeapSize },
{ "getNativeHeapAllocatedSize", "()J",
(void*) android_os_Debug_getNativeHeapAllocatedSize },
{ "getNativeHeapFreeSize", "()J",
(void*) android_os_Debug_getNativeHeapFreeSize },
{ "getMemoryInfo", "(Landroid/os/Debug$MemoryInfo;)V",
(void*) android_os_Debug_getDirtyPages },
...
};
找到jni对应函数,先看getMemoryInfo
static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object)
{
android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object);
}
static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
jint pid, jobject object)
{
bool foundSwapPss;
stats_t stats[_NUM_HEAP];
memset(&stats, 0, sizeof(stats));
load_maps(pid, stats, &foundSwapPss);
struct graphics_memory_pss graphics_mem;
if (read_memtrack_memory(pid, &graphics_mem) == 0) {
stats[HEAP_GRAPHICS].pss = graphics_mem.graphics;
stats[HEAP_GRAPHICS].privateDirty = graphics_mem.graphics;
stats[HEAP_GL].pss = graphics_mem.gl;
stats[HEAP_GL].privateDirty = graphics_mem.gl;
stats[HEAP_OTHER_MEMTRACK].pss = graphics_mem.other;
stats[HEAP_OTHER_MEMTRACK].privateDirty = graphics_mem.other;
}
for (int i=_NUM_CORE_HEAP; i<_NUM_EXCLUSIVE_HEAP; i++) {
stats[HEAP_UNKNOWN].pss += stats[i].pss;
stats[HEAP_UNKNOWN].swappablePss += stats[i].swappablePss;
stats[HEAP_UNKNOWN].privateDirty += stats[i].privateDirty;
stats[HEAP_UNKNOWN].sharedDirty += stats[i].sharedDirty;
stats[HEAP_UNKNOWN].privateClean += stats[i].privateClean;
stats[HEAP_UNKNOWN].sharedClean += stats[i].sharedClean;
stats[HEAP_UNKNOWN].swappedOut += stats[i].swappedOut;
stats[HEAP_UNKNOWN].swappedOutPss += stats[i].swappedOutPss;
}
for (int i=0; i<_NUM_CORE_HEAP; i++) {
env->SetIntField(object, stat_fields[i].pss_field, stats[i].pss);
env->SetIntField(object, stat_fields[i].pssSwappable_field, stats[i].swappablePss);
env->SetIntField(object, stat_fields[i].privateDirty_field, stats[i].privateDirty);
env->SetIntField(object, stat_fields[i].sharedDirty_field, stats[i].sharedDirty);
env->SetIntField(object, stat_fields[i].privateClean_field, stats[i].privateClean);
env->SetIntField(object, stat_fields[i].sharedClean_field, stats[i].sharedClean);
env->SetIntField(object, stat_fields[i].swappedOut_field, stats[i].swappedOut);
env->SetIntField(object, stat_fields[i].swappedOutPss_field, stats[i].swappedOutPss);
}
env->SetBooleanField(object, hasSwappedOutPss_field, foundSwapPss);
jintArray otherIntArray = (jintArray)env->GetObjectField(object, otherStats_field);
jint* otherArray = (jint*)env->GetPrimitiveArrayCritical(otherIntArray, 0);
if (otherArray == NULL) {
return;
}
int j=0;
for (int i=_NUM_CORE_HEAP; i<_NUM_HEAP; i++) {
otherArray[j++] = stats[i].pss;
otherArray[j++] = stats[i].swappablePss;
otherArray[j++] = stats[i].privateDirty;
otherArray[j++] = stats[i].sharedDirty;
otherArray[j++] = stats[i].privateClean;
otherArray[j++] = stats[i].sharedClean;
otherArray[j++] = stats[i].swappedOut;
otherArray[j++] = stats[i].swappedOutPss;
}
env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0);
}
先通过load_maps打开/proc/PID/smaps虚拟文件,读取内部信息,root手机可以通过adb shell cat/proce/PID/smaps查看,它输出的信息实际上是应用的用户空间的内存分配表,记录了应用分配的每一块内存的地址,类别,大小等信息,而load_maps方法调用read_mapinfo方法从这个表里面读出每一块内存的分配信息,分类进行累加,得出Native Heap,DalvikHeap等各个类别的内存占用。
再看看几个获取native内存的方法:
static jlong android_os_Debug_getNativeHeapSize(JNIEnv *env, jobject clazz)
{
struct mallinfo info = mallinfo();
return (jlong) info.usmblks;
}
static jlong android_os_Debug_getNativeHeapAllocatedSize(JNIEnv *env, jobject clazz)
{
struct mallinfo info = mallinfo();
return (jlong) info.uordblks;
}
static jlong android_os_Debug_getNativeHeapFreeSize(JNIEnv *env, jobject clazz)
{
struct mallinfo info = mallinfo();
return (jlong) info.fordblks;
}
这里统一是通过mallinfo()获取的。
struct mallinfo {
int arena; /* non-mmapped space allocated from system */ //当前从系统分配的非mmapped字节总数
int ordblks; /* number of free chunks */ //空闲块的数量
int smblks; /* number of fastbin blocks */ //fastbin块的数量
int hblks; /* number of mmapped regions */ //mmapped区域的当前数量
int hblkhd; /* space in mmapped regions */ //在mmapped区域中保存的字节总数
int usmblks; /* maximum total allocated space */ //分配的最大总空间。
int fsmblks; /* space available in freed fastbin blocks */
int uordblks; /* total allocated space */ //当前分配的总空间
int fordblks; /* total free space */ //总空闲空间
int keepcost; /* top-most, releasable (via malloc_trim) space */ //理想情况下可以释放的最大字节数通过malloc_trim返回系统
};
这里对照mallinfo字段:
android_os_Debug_getNativeHeapSize ------ info.usmblks 分配的最大总空间
android_os_Debug_getNativeHeapAllocatedSize -------- info.uordblks 当前分配的总空间
android_os_Debug_getNativeHeapFreeSize ------- info.fordblks 总空闲空间
3.3 总结:
Debug主要能获取到三大块内容:
adb shell dumpsys meminfo pid1.以Debug.getNativeHeapSize() 为代表的获取native内存信息,底层主要是通过mallinfo获取的。
2.Debug.getMemoryInfo(mi)执行之后,以mi.getTotalPss()为代表的获取从/proc/PID/smaps统计到的内存信息。
3.Debug.getMemoryInfo(mi)执行之后,以mi.getMemoryStat("summary.java-heap”)为代表的2数据的整合。
网友评论