這里簡單總結(jié)下Android獲取內(nèi)存的方式,方式僅限于系統(tǒng)開放給應(yīng)用層的API,adb命令比較簡單,不在本題討論范圍內(nèi),想了解的可以參考之前文章:性能優(yōu)化工具(十)- Android內(nèi)存分析命令。
一、AMS獲取內(nèi)存信息
1.1 獲取方式
ActivityManager am = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
am.getMemoryInfo(memoryInfo);
memoryInfo.totalMem//系統(tǒng)總內(nèi)存
memoryInfo.availMem//可使用內(nèi)存
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方法具體實現(xiàn):
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文件讀取內(nèi)存信息:
adb shell cat proc/meminfo
MemTotal: 3721316 kB
MemFree: 132652 kB
MemAvailable: 1250484 kB
Buffers: 87284 kB
Cached: 1205132 kB
…
1.3 總結(jié)
memoryInfo.totalMem = MemTotal
memoryInfo.availMem = Cached+MemFree
二、Runtime獲取內(nèi)存信息
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();
看看具體實現(xiàn):
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_;
}
}
}
這里執(zhí)行的是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_:當前階段最大已申請的內(nèi)存大小
//GetBytesAllocated():當前已分配內(nèi)存大小
return std::max(max_allowed_footprint_, GetBytesAllocated());
}
totalMemory也就是指當前虛擬機進程已經(jīng)向系統(tǒng)申請的內(nèi)存大小。
art/runtime/gc/heap.h
// Returns the number of bytes currently allocated.
size_t GetBytesAllocated() const {
return num_bytes_allocated_.LoadSequentiallyConsistent();//當前分配內(nèi)存大小
}
// 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.
//當前已分配內(nèi)存 與 增長上限的最大值。其實也就是虛擬機最大可向系統(tǒng)申請到的內(nèi)存大小
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 - 已分配的內(nèi)存
return total_memory - std::min(total_memory, byte_allocated);
}
2.3 總結(jié)
runtime.maxMemory()//獲取虛擬機最大分配內(nèi)存
runtime.totalMemory()//獲取虛擬機當前申請到的內(nèi)存大小
runtime.freeMemory()//獲取當前申請內(nèi)存的剩余內(nèi)存大小
距離oom還剩余的可分配內(nèi)存空間:
runtime.maxMemory() - (runtime.totalMemory() - runtime.freeMemory())
三、Debug獲取內(nèi)存信息
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獲取內(nèi)存的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 },
...
};
找到j(luò)ni對應(yīng)函數(shù),先看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虛擬文件,讀取內(nèi)部信息,root手機可以通過adb shell cat/proce/PID/smaps查看,它輸出的信息實際上是應(yīng)用的用戶空間的內(nèi)存分配表,記錄了應(yīng)用分配的每一塊內(nèi)存的地址,類別,大小等信息,而load_maps方法調(diào)用read_mapinfo方法從這個表里面讀出每一塊內(nèi)存的分配信息,分類進行累加,得出Native Heap,DalvikHeap等各個類別的內(nèi)存占用。
再看看幾個獲取native內(nèi)存的方法:
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;
}
這里統(tǒng)一是通過mallinfo()獲取的。
struct mallinfo {
int arena; /* non-mmapped space allocated from system */ //當前從系統(tǒng)分配的非mmapped字節(jié)總數(shù)
int ordblks; /* number of free chunks */ //空閑塊的數(shù)量
int smblks; /* number of fastbin blocks */ //fastbin塊的數(shù)量
int hblks; /* number of mmapped regions */ //mmapped區(qū)域的當前數(shù)量
int hblkhd; /* space in mmapped regions */ //在mmapped區(qū)域中保存的字節(jié)總數(shù)
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 */ //理想情況下可以釋放的最大字節(jié)數(shù)通過malloc_trim返回系統(tǒng)
};
這里對照mallinfo字段:
android_os_Debug_getNativeHeapSize ------ info.usmblks 分配的最大總空間
android_os_Debug_getNativeHeapAllocatedSize -------- info.uordblks 當前分配的總空間
android_os_Debug_getNativeHeapFreeSize ------- info.fordblks 總空閑空間
3.3 總結(jié):
Debug主要能獲取到三大塊內(nèi)容:

1.以Debug.getNativeHeapSize() 為代表的獲取native內(nèi)存信息,底層主要是通過mallinfo獲取的。
2.Debug.getMemoryInfo(mi)執(zhí)行之后,以mi.getTotalPss()為代表的獲取從/proc/PID/smaps統(tǒng)計到的內(nèi)存信息。
3.Debug.getMemoryInfo(mi)執(zhí)行之后,以mi.getMemoryStat("summary.java-heap”)為代表的2數(shù)據(jù)的整合。