问题描述
为了允许多个进程同时运行,Android 针对为每个应用分配的堆大小设置了硬性限制。设备的确切堆大小限制因设备总体可用的 RAM 容量而异。如果您的应用达到堆容量上限并尝试分配更多内存,系统就会抛出 OutOfMemoryError。
I .gpadc.yolodem: Alloc young concurrent copying GC freed 6910(352KB) AllocSpace objects, 17(1984KB) LOS objects, 0% free, 764MB/768MB, paused 41us,26us total 7.151ms
I .gpadc.yolodem: Forcing collection of SoftReferences for 22MB allocation
I .gpadc.yolodem: Starting a blocking GC Alloc
I .gpadc.yolodem: Clamp target GC heap from 785MB to 768MB
I .gpadc.yolodem: Alloc concurrent copying GC freed 3041(179KB) AllocSpace objects, 5(3200KB) LOS objects, 0% free, 761MB/768MB, paused 25us,32us total 10.123ms
W .gpadc.yolodem: Throwing OutOfMemoryError "Failed to allocate a 23969804 byte allocation with 7320896 free bytes and 7149KB until OOM, target footprint 805306368, growth limit 805306368" (VmSize 3915852 kB)
I .gpadc.yolodem: Starting a blocking GC Alloc
I .gpadc.yolodem: Starting a blocking GC Alloc
I .gpadc.yolodem: Alloc young concurrent copying GC freed 10(59KB) AllocSpace objects, 1(20KB) LOS objects, 0% free, 761MB/768MB, paused 26us,21us total 5.686ms
I .gpadc.yolodem: Forcing collection of SoftReferences for 22MB allocation
I .gpadc.yolodem: Starting a blocking GC Alloc
I .gpadc.yolodem: Clamp target GC heap from 785MB to 768MB
I .gpadc.yolodem: Alloc concurrent copying GC freed 5053(117KB) AllocSpace objects, 10(848KB) LOS objects, 0% free, 761MB/768MB, paused 28us,26us total 9.191ms
W .gpadc.yolodem: Throwing OutOfMemoryError "Failed to allocate a 23969808 byte allocation with 7281440 free bytes and 7110KB until OOM, target footprint 805306368, growth limit 805306368" (VmSize 3912580 kB)
D AndroidRuntime: Shutting down VM
E AndroidRuntime: FATAL EXCEPTION: main
E AndroidRuntime: Process: com.rockchip.gpadc.yolodemo, PID: 2270
E AndroidRuntime: java.lang.OutOfMemoryError: Failed to allocate a 23969808 byte allocation with 7281440 free bytes and 7110KB until OOM, target footprint 805306368, growth limit 805306368
E AndroidRuntime: at com.rockchip.gpadc.demo.gige.HKCameraControl.getOneBmp(HKCameraControl.java:519)
E AndroidRuntime: at com.rockchip.gpadc.app.OcrPositionAct.ocrTakePicture(OcrPositionAct.kt:557)
E AndroidRuntime: at com.rockchip.gpadc.app.OcrPositionAct.dealTakePhoto$lambda$5(OcrPositionAct.kt:419)
E AndroidRuntime: at com.rockchip.gpadc.app.OcrPositionAct.$r8$lambda$BRclX27CrRub3uN3h50Ovr231Vo(Unknown Source:0)
E AndroidRuntime: at com.rockchip.gpadc.app.OcrPositionAct$$ExternalSyntheticLambda6.run(Unknown Source:8)
E AndroidRuntime: at android.os.Handler.handleCallback(Handler.java:938)
E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:99)
E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:201)
E AndroidRuntime: at android.os.Looper.loop(Looper.java:288)
E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:7870)
E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
OOM 源码位置
art/runtime/gc/heap.cc
void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, AllocatorType allocator_type) {
// If we're in a stack overflow, do not create a new exception. It would require running the
// constructor, which will of course still be in a stack overflow.
if (self->IsHandlingStackOverflow()) {
self->SetException(
Runtime::Current()->GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow());
return;
}
std::ostringstream oss;
size_t total_bytes_free = GetFreeMemory();
oss << "Failed to allocate a " << byte_count << " byte allocation with " << total_bytes_free
<< " free bytes and " << PrettySize(GetFreeMemoryUntilOOME()) << " until OOM,"
<< " target footprint " << target_footprint_.load(std::memory_order_relaxed)
<< ", growth limit "
<< growth_limit_;
// If the allocation failed due to fragmentation, print out the largest continuous allocation.
if (total_bytes_free >= byte_count) {
space::AllocSpace* space = nullptr;
if (allocator_type == kAllocatorTypeNonMoving) {
space = non_moving_space_;
} else if (allocator_type == kAllocatorTypeRosAlloc ||
allocator_type == kAllocatorTypeDlMalloc) {
space = main_space_;
} else if (allocator_type == kAllocatorTypeBumpPointer ||
allocator_type == kAllocatorTypeTLAB) {
space = bump_pointer_space_;
} else if (allocator_type == kAllocatorTypeRegion ||
allocator_type == kAllocatorTypeRegionTLAB) {
space = region_space_;
}
// There is no fragmentation info to log for large-object space.
if (allocator_type != kAllocatorTypeLOS) {
CHECK(space != nullptr) << "allocator_type:" << allocator_type
<< " byte_count:" << byte_count
<< " total_bytes_free:" << total_bytes_free;
// LogFragmentationAllocFailure returns true if byte_count is greater than
// the largest free contiguous chunk in the space. Return value false
// means that we are throwing OOME because the amount of free heap after
// GC is less than kMinFreeHeapAfterGcForAlloc in proportion of the heap-size.
// Log an appropriate message in that case.
if (!space->LogFragmentationAllocFailure(oss, byte_count)) {
oss << "; giving up on allocation because <"
<< kMinFreeHeapAfterGcForAlloc * 100
<< "% of heap free after GC.";
}
}
}
self->ThrowOutOfMemoryError(oss.str().c_str());
}
单个应用内存上限获取
App接口
public void doSomethingMemoryIntensive() {
// Before doing something that requires a lot of memory,
// check whether the device is in a low memory state.
ActivityManager.MemoryInfo memoryInfo = getAvailableMemory();
if (!memoryInfo.lowMemory) {
// Do memory intensive work.
}
}
// Get a MemoryInfo object for the device's current memory status.
private ActivityManager.MemoryInfo getAvailableMemory() {
ActivityManager activityManager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);
return memoryInfo;
}
framework
ActivityManager.java
@SystemService(Context.ACTIVITY_SERVICE)
public class ActivityManager {
private static String TAG = "ActivityManager";
public static class MemoryInfo implements Parcelable {
/**
* The available memory on the system. This number should not
* be considered absolute: due to the nature of the kernel, a significant
* portion of this memory is actually in use and needed for the overall
* system to run well.
*/
public long availMem;
/**
* The total memory accessible by the kernel. This is basically the
* RAM size of the device, not including below-kernel fixed allocations
* like DMA buffers, RAM for the baseband CPU, etc.
*/
public long totalMem;
/**
* The threshold of {@link #availMem} at which we consider memory to be
* low and start killing background services and other non-extraneous
* processes.
*/
public long threshold;
/**
* Set to true if the system considers itself to currently be in a low
* memory situation.
*/
public boolean lowMemory;
}
}
ActivityManagerService.java
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback, ActivityManagerGlobalLock {
/**
* Process management.
*/
final ProcessList mProcessList;
public ActivityManagerService(Injector injector, ServiceThread handlerThread) {
mProcessList = injector.getProcessList(this);
mProcessList.init(this, activeUids, mPlatformCompat);
}
@Override
public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
mProcessList.getMemoryInfo(outInfo);
}
}
ProcessList.java
/**
* Activity manager code dealing with processes.
*/
public final class ProcessList {
void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
final long homeAppMem = getMemLevel(HOME_APP_ADJ);
final long cachedAppMem = getMemLevel(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 = getMemLevel(SERVICE_ADJ);
outInfo.visibleAppThreshold = getMemLevel(VISIBLE_APP_ADJ);
outInfo.foregroundAppThreshold = getMemLevel(FOREGROUND_APP_ADJ);
}
}
Process.java
/**
* Tools for managing OS processes.
*/
public class Process {
/** @hide */
@UnsupportedAppUsage
public static final native long getFreeMemory();
/** @hide */
@UnsupportedAppUsage
public static final native long getTotalMemory();
}
./base/core/jni/android_util_Process.cpp
static jlong android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
{
std::array<std::string_view, 2> memFreeTags = {
::android::meminfo::SysMemInfo::kMemFree,
::android::meminfo::SysMemInfo::kMemCached,
};
std::vector<uint64_t> mem(memFreeTags.size());
::android::meminfo::SysMemInfo smi;
if (!smi.ReadMemInfo(memFreeTags.size(),
memFreeTags.data(),
mem.data())) {
jniThrowRuntimeException(env, "SysMemInfo read failed to get Free Memory");
return -1L;
}
jlong sum = 0;
std::for_each(mem.begin(), mem.end(), [&](uint64_t val) { sum += val; });
return sum * 1024;
}
static jlong android_os_Process_getTotalMemory(JNIEnv* env, jobject clazz)
{
struct sysinfo si;
if (sysinfo(&si) == -1) {
ALOGE("sysinfo failed: %s", strerror(errno));
return -1;
}
return static_cast<jlong>(si.totalram) * si.mem_unit;
}
./kernel-5.10/include/uapi/linux/sysinfo.h
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#ifndef _LINUX_SYSINFO_H
#define _LINUX_SYSINFO_H
#include <linux/types.h>
#define SI_LOAD_SHIFT 16
struct sysinfo {
__kernel_long_t uptime; /* Seconds since boot */
__kernel_ulong_t loads[3]; /* 1, 5, and 15 minute load averages */
__kernel_ulong_t totalram; /* Total usable main memory size */
__kernel_ulong_t freeram; /* Available memory size */
__kernel_ulong_t sharedram; /* Amount of shared memory */
__kernel_ulong_t bufferram; /* Memory used by buffers */
__kernel_ulong_t totalswap; /* Total swap space size */
__kernel_ulong_t freeswap; /* swap space still available */
__u16 procs; /* Number of current processes */
__u16 pad; /* Explicit padding for m68k */
__kernel_ulong_t totalhigh; /* Total high memory size */
__kernel_ulong_t freehigh; /* Available high memory size */
__u32 mem_unit; /* Memory unit size in bytes */
char _f[20-2*sizeof(__kernel_ulong_t)-sizeof(__u32)]; /* Padding: libc5 uses this.. */
};
#endif /* _LINUX_SYSINFO_H */
dumpsys meminfo
Applications Memory Usage (in Kilobytes):
Uptime: 6294345 Realtime: 6294345
** MEMINFO in pid 2611 [com.rockchip.gpadc.yolodemo] **
Pss Pss Shared Private Shared Private Swap Rss Heap Heap Heap
Total Clean Dirty Dirty Clean Clean Dirty Total Size Alloc Free
------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------
Native Heap 46498 0 2516 46000 0 0 0 48516 63076 50190 2420
Dalvik Heap 2660 0 8264 1020 0 0 0 9284 4120 2060 2060
Dalvik Other 938 0 1544 692 0 0 0 2236
Stack 441 0 12 440 0 0 0 452
Ashmem 23 0 532 0 4 0 0 536
Other dev 365560 0 272 209836 0 155724 0 365832
.so mmap 22200 10628 4208 196 34592 10628 0 49624
.jar mmap 1194 0 0 0 23624 0 0 23624
.apk mmap 13048 11936 0 676 2016 11936 0 14628
.ttf mmap 30 0 0 0 152 0 0 152
.dex mmap 8420 8380 12 4 444 8380 0 8840
.oat mmap 3341 256 64 0 11516 256 0 11836
.art mmap 3386 0 14204 512 136 0 0 14852
Other mmap 158 0 32 4 788 64 0 888
Unknown 1765 0 544 1672 0 0 0 2216
TOTAL 469662 31200 32204 261052 73272 186988 0 553516 67196 52250 4480
Dalvik Details
.Heap 584 0 0 584 0 0 0 584
.LOS 352 0 1772 12 0 0 0 1784
.Zygote 1704 0 6492 404 0 0 0 6896
.NonMoving 20 0 0 20 0 0 0 20
.LinearAlloc 792 0 1396 572 0 0 0 1968
.GC 77 0 108 56 0 0 0 164
.ZygoteJIT 5 0 36 0 0 0 0 36
.AppJIT 16 0 0 16 0 0 0 16
.IndirectRef 48 0 4 48 0 0 0 52
.Boot vdex 23 0 0 0 416 0 0 416
.App dex 116 100 12 4 24 100 0 140
.App vdex 8281 8280 0 0 4 8280 0 8284
.Boot art 3386 0 14204 512 136 0 0 14852
App Summary
Pss(KB) Rss(KB)
------ ------
Java Heap: 1532 24136
Native Heap: 46000 48516
Code: 32092 108756
Stack: 440 452
Graphics: 0 0
Private Other: 367976
System: 21622
Unknown: 371656
TOTAL PSS: 469662 TOTAL RSS: 553516 TOTAL SWAP (KB): 0
Objects
Views: 7 ViewRootImpl: 1
AppContexts: 6 Activities: 2
Assets: 8 AssetManagers: 0
Local Binders: 9 Proxy Binders: 33
Parcel memory: 4 Parcel count: 18
Death Recipients: 0 OpenSSL Sockets: 0
WebViews: 0
SQL
MEMORY_USED: 0
PAGECACHE_OVERFLOW: 0 MALLOC_SIZE: 0
----------------------------------------------------------------------------------------------------------
Total RSS by process:
553,516K: com.rockchip.gpadc.yolodemo (pid 2611 / activities)
514,020K: system (pid 625)
269,168K: com.android.systemui (pid 796)
215,320K: com.android.launcher3 (pid 1127 / activities)
194,544K: zygote64 (pid 422)
173,916K: zygote (pid 423)
Total RSS by OOM adjustment:
1,048,724K: Native
194,544K: zygote64 (pid 422)
173,916K: zygote (pid 423)
74,204K: surfaceflinger (pid 331)
Total RSS by category:
1,362,276K: .so mmap
743,264K: .jar mmap
537,972K: .art mmap
532,072K: .Boot art
5,900K: .App art
389,164K: Other dev
382,176K: Native
338,644K: .oat mmap
289,948K: Dalvik
201,276K: .Zygote
50,308K: .LOS
35,456K: .Heap
2,908K: .NonMoving
231,312K: .apk mmap
149,404K: .dex mmap
121,736K: .App dex
14,844K: .App vdex
12,824K: .Boot vdex
138,252K: Other mmap
87,192K: Dalvik Other
74,404K: .LinearAlloc
5,484K: .GC
4,132K: .AppJIT
1,900K: .IndirectRef
1,272K: .ZygoteJIT
0K: .CompilerMetadata
60,712K: Unknown
20,228K: Stack
14,280K: Ashmem
12,600K: .ttf mmap
0K: Cursor
0K: Gfx dev
0K: EGL mtrack
0K: GL mtrack
0K: Other mtrack
Total RAM: 16,071,376K (status normal)
Free RAM: 14,102,789K ( 96,005K cached pss + 832,840K cached kernel + 13,173,944K free)
DMA-BUF: 455,268K ( 0K mapped + 455,268K unmapped)
DMA-BUF Heaps: 89,724K
DMA-BUF Heaps pool: 98,980K
Used RAM: 2,447,645K (1,393,609K used pss + 1,054,036K kernel)
Lost RAM: -479,062K
ZRAM: 4K physical used for 0K in swap (8,035,684K total swap)
Tuning: 256 (large 768), oom 322,560K, restore limit 107,520K (high-end-gfx)
流程图
源码
./base/core/java/android/os/Debug.java
public final class Debug
{
* 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);
/**
* Note: currently only works when the requested pid has the same UID
* as the caller.
*
* @return true if the meminfo was read successfully, false if not (i.e., given pid has gone).
*
* @hide
*/
@UnsupportedAppUsage
public static native boolean getMemoryInfo(int pid, MemoryInfo memoryInfo);
}
./native/libs/binder/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 },
{ "getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)Z",
(void*) android_os_Debug_getDirtyPagesPid },
}
static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object)
{
android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object);
}
static jboolean android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
jint pid, jobject object)
{
bool foundSwapPss;
stats_t stats[_NUM_HEAP];
memset(&stats, 0, sizeof(stats));
// 该方法的主要业务逻辑是遍历/proc/pid/smaps,根据内存的不同分类,计算出各个类别的内存占用情况
if (!load_maps(pid, stats, &foundSwapPss)) {
return JNI_FALSE;
}
// 获取没有归属到/proc/pid/smaps中的graphics,gl,other类别的内存。
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_GRAPHICS].rss = graphics_mem.graphics;
stats[HEAP_GL].pss = graphics_mem.gl;
stats[HEAP_GL].privateDirty = graphics_mem.gl;
stats[HEAP_GL].rss = graphics_mem.gl;
stats[HEAP_OTHER_MEMTRACK].pss = graphics_mem.other;
stats[HEAP_OTHER_MEMTRACK].privateDirty = graphics_mem.other;
stats[HEAP_OTHER_MEMTRACK].rss = graphics_mem.other;
}
// 将包括graphics,gl,other在内的内存统一归并到HEAP_UNKNOWN
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].rss += stats[i].rss;
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;
}
// 将HEAP_UNKNOWN, HEAP_DALVIK, HEAP_NATIVE传递给Java层的MemoryInfo对应的实例
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].rss_field, stats[i].rss);
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 JNI_FALSE;
}
// 将HEAP_DALVIK_OTHER到HEAP_OTHER_MEMTRACK的内存数据传递给Java层的otherStats数组中
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].rss;
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);
return JNI_TRUE;
}
//--------------------------------------------/proc/%d/smaps------------------------------------------
static bool load_maps(int pid, stats_t* stats, bool* foundSwapPss)
{
*foundSwapPss = false;
uint64_t prev_end = 0;
int prev_heap = HEAP_UNKNOWN;
std::string smaps_path = base::StringPrintf("/proc/%d/smaps", pid);
auto vma_scan = [&](const meminfo::Vma& vma) {
int which_heap = HEAP_UNKNOWN;
int sub_heap = HEAP_UNKNOWN;
bool is_swappable = false;
std::string name;
if (base::EndsWith(vma.name, " (deleted)")) {
name = vma.name.substr(0, vma.name.size() - strlen(" (deleted)"));
} else {
name = vma.name;
}
uint32_t namesz = name.size();
if (base::StartsWith(name, "[heap]")) {
which_heap = HEAP_NATIVE;
} else if (base::StartsWith(name, "[anon:libc_malloc]")) {
which_heap = HEAP_NATIVE;
} else if (base::StartsWith(name, "[anon:scudo:")) {
which_heap = HEAP_NATIVE;
} else if (base::StartsWith(name, "[anon:GWP-ASan")) {
which_heap = HEAP_NATIVE;
} else if (base::StartsWith(name, "[stack")) {
which_heap = HEAP_STACK;
} else if (base::StartsWith(name, "[anon:stack_and_tls:")) {
which_heap = HEAP_STACK;
} else if (base::EndsWith(name, ".so")) {
which_heap = HEAP_SO;
is_swappable = true;
} else if (base::EndsWith(name, ".jar")) {
which_heap = HEAP_JAR;
is_swappable = true;
} else if (base::EndsWith(name, ".apk")) {
which_heap = HEAP_APK;
is_swappable = true;
} else if (base::EndsWith(name, ".ttf")) {
which_heap = HEAP_TTF;
is_swappable = true;
} else if ((base::EndsWith(name, ".odex")) ||
(namesz > 4 && strstr(name.c_str(), ".dex") != nullptr)) {
which_heap = HEAP_DEX;
sub_heap = HEAP_DEX_APP_DEX;
is_swappable = true;
} else if (base::EndsWith(name, ".vdex")) {
which_heap = HEAP_DEX;
// Handle system@framework@boot and system/framework/boot|apex
if ((strstr(name.c_str(), "@boot") != nullptr) ||
(strstr(name.c_str(), "/boot") != nullptr) ||
(strstr(name.c_str(), "/apex") != nullptr)) {
sub_heap = HEAP_DEX_BOOT_VDEX;
} else {
sub_heap = HEAP_DEX_APP_VDEX;
}
is_swappable = true;
} else if (base::EndsWith(name, ".oat")) {
which_heap = HEAP_OAT;
is_swappable = true;
} else if (base::EndsWith(name, ".art") || base::EndsWith(name, ".art]")) {
which_heap = HEAP_ART;
// Handle system@framework@boot* and system/framework/boot|apex*
if ((strstr(name.c_str(), "@boot") != nullptr) ||
(strstr(name.c_str(), "/boot") != nullptr) ||
(strstr(name.c_str(), "/apex") != nullptr)) {
sub_heap = HEAP_ART_BOOT;
} else {
sub_heap = HEAP_ART_APP;
}
is_swappable = true;
} else if (base::StartsWith(name, "/dev/")) {
which_heap = HEAP_UNKNOWN_DEV;
if (base::StartsWith(name, "/dev/kgsl-3d0")) {
which_heap = HEAP_GL_DEV;
} else if (base::StartsWith(name, "/dev/ashmem/CursorWindow")) {
which_heap = HEAP_CURSOR;
} else if (base::StartsWith(name, "/dev/ashmem/jit-zygote-cache")) {
which_heap = HEAP_DALVIK_OTHER;
sub_heap = HEAP_DALVIK_OTHER_ZYGOTE_CODE_CACHE;
} else if (base::StartsWith(name, "/dev/ashmem")) {
which_heap = HEAP_ASHMEM;
}
} else if (base::StartsWith(name, "/memfd:jit-cache")) {
which_heap = HEAP_DALVIK_OTHER;
sub_heap = HEAP_DALVIK_OTHER_APP_CODE_CACHE;
} else if (base::StartsWith(name, "/memfd:jit-zygote-cache")) {
which_heap = HEAP_DALVIK_OTHER;
sub_heap = HEAP_DALVIK_OTHER_ZYGOTE_CODE_CACHE;
} else if (base::StartsWith(name, "[anon:")) {
which_heap = HEAP_UNKNOWN;
if (base::StartsWith(name, "[anon:dalvik-")) {
which_heap = HEAP_DALVIK_OTHER;
if (base::StartsWith(name, "[anon:dalvik-LinearAlloc")) {
sub_heap = HEAP_DALVIK_OTHER_LINEARALLOC;
} else if (base::StartsWith(name, "[anon:dalvik-alloc space") ||
base::StartsWith(name, "[anon:dalvik-main space")) {
// This is the regular Dalvik heap.
which_heap = HEAP_DALVIK;
sub_heap = HEAP_DALVIK_NORMAL;
} else if (base::StartsWith(name,
"[anon:dalvik-large object space") ||
base::StartsWith(
name, "[anon:dalvik-free list large object space")) {
which_heap = HEAP_DALVIK;
sub_heap = HEAP_DALVIK_LARGE;
} else if (base::StartsWith(name, "[anon:dalvik-non moving space")) {
which_heap = HEAP_DALVIK;
sub_heap = HEAP_DALVIK_NON_MOVING;
} else if (base::StartsWith(name, "[anon:dalvik-zygote space")) {
which_heap = HEAP_DALVIK;
sub_heap = HEAP_DALVIK_ZYGOTE;
} else if (base::StartsWith(name, "[anon:dalvik-indirect ref")) {
sub_heap = HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE;
} else if (base::StartsWith(name, "[anon:dalvik-jit-code-cache") ||
base::StartsWith(name, "[anon:dalvik-data-code-cache")) {
sub_heap = HEAP_DALVIK_OTHER_APP_CODE_CACHE;
} else if (base::StartsWith(name, "[anon:dalvik-CompilerMetadata")) {
sub_heap = HEAP_DALVIK_OTHER_COMPILER_METADATA;
} else {
sub_heap = HEAP_DALVIK_OTHER_ACCOUNTING; // Default to accounting.
}
}
} else if (namesz > 0) {
which_heap = HEAP_UNKNOWN_MAP;
} else if (vma.start == prev_end && prev_heap == HEAP_SO) {
// bss section of a shared library
which_heap = HEAP_SO;
}
prev_end = vma.end;
prev_heap = which_heap;
const meminfo::MemUsage& usage = vma.usage;
if (usage.swap_pss > 0 && *foundSwapPss != true) {
*foundSwapPss = true;
}
uint64_t swapable_pss = 0;
if (is_swappable && (usage.pss > 0)) {
float sharing_proportion = 0.0;
if ((usage.shared_clean > 0) || (usage.shared_dirty > 0)) {
sharing_proportion = (usage.pss - usage.uss) / (usage.shared_clean + usage.shared_dirty);
}
swapable_pss = (sharing_proportion * usage.shared_clean) + usage.private_clean;
}
stats[which_heap].pss += usage.pss;
stats[which_heap].swappablePss += swapable_pss;
stats[which_heap].rss += usage.rss;
stats[which_heap].privateDirty += usage.private_dirty;
stats[which_heap].sharedDirty += usage.shared_dirty;
stats[which_heap].privateClean += usage.private_clean;
stats[which_heap].sharedClean += usage.shared_clean;
stats[which_heap].swappedOut += usage.swap;
stats[which_heap].swappedOutPss += usage.swap_pss;
if (which_heap == HEAP_DALVIK || which_heap == HEAP_DALVIK_OTHER ||
which_heap == HEAP_DEX || which_heap == HEAP_ART) {
stats[sub_heap].pss += usage.pss;
stats[sub_heap].swappablePss += swapable_pss;
stats[sub_heap].rss += usage.rss;
stats[sub_heap].privateDirty += usage.private_dirty;
stats[sub_heap].sharedDirty += usage.shared_dirty;
stats[sub_heap].privateClean += usage.private_clean;
stats[sub_heap].sharedClean += usage.shared_clean;
stats[sub_heap].swappedOut += usage.swap;
stats[sub_heap].swappedOutPss += usage.swap_pss;
}
};
return meminfo::ForEachVmaFromFile(smaps_path, vma_scan);
}
rk3588 memtrack
./system/memory/libmemtrack/memtrack.cpp
HAL层接口
./hardware/interfaces/memtrack/
hardware/interfaces/memtrack$ tree
.
├── 1.0
│ ├── Android.bp
│ ├── default
│ │ ├── Android.bp
│ │ ├── android.hardware.memtrack@1.0-service.rc
│ │ ├── Memtrack.cpp
│ │ ├── Memtrack.h
│ │ └── service.cpp
│ ├── IMemtrack.hal
│ ├── types.hal
│ └── vts
│ └── functional
│ ├── Android.bp
│ └── VtsHalMemtrackV1_0TargetTest.cpp
└── aidl
├── aidl_api
│ └── android.hardware.memtrack
│ ├── 1
│ │ └── android
│ │ └── hardware
│ │ └── memtrack
│ │ ├── DeviceInfo.aidl
│ │ ├── IMemtrack.aidl
│ │ ├── MemtrackRecord.aidl
│ │ └── MemtrackType.aidl
│ └── current
│ └── android
│ └── hardware
│ └── memtrack
│ ├── DeviceInfo.aidl
│ ├── IMemtrack.aidl
│ ├── MemtrackRecord.aidl
│ └── MemtrackType.aidl
├── android
│ └── hardware
│ └── memtrack
│ ├── DeviceInfo.aidl
│ ├── IMemtrack.aidl
│ ├── MemtrackRecord.aidl
│ └── MemtrackType.aidl
├── Android.bp
├── default
│ ├── Android.bp
│ ├── main.cpp
│ ├── Memtrack.cpp
│ ├── memtrack-default.rc
│ ├── memtrack-default.xml
│ └── Memtrack.h
└── vts
├── Android.bp
└── VtsHalMemtrackTargetTest.cpp
RK的具体实现
hardware/rockchip/libmemtrack$ tree
.
├── Android.mk
├── memtrack.cpp
├── memtrack.h
├── rk322x.cpp
├── rk3326.cpp
├── rk3399.cpp
└── rk_common.cpp
Android 资源限制命令
rk3588_s:/ $ ulimit -a
-t: time(cpu-seconds) unlimited
-f: file(blocks) unlimited
-c: coredump(blocks) 0
-d: data(KiB) unlimited
-s: stack(KiB) 8192
-l: lockedmem(KiB) 65536
-n: nofiles(descriptors) 32768
-p: processes 62250
-i: sigpending 62250
-q: msgqueue(bytes) 819200
-e: maxnice 40
-r: maxrtprio 0
-m: resident-set(KiB) unlimited
-v: address-space(KiB) unlimited
-x: filelocks unlimited
rk3588_s:/ $ cat /proc/2613/limits
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size 0 unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 62250 62250 processes
Max open files 32768 32768 files // 表示可以在进程中打开文件的数量
Max locked memory 67108864 67108864 bytes
Max address space unlimited unlimited bytes
Max file locks unlimited unlimited locks
Max pending signals 62250 62250 signals
Max msgqueue size 819200 819200 bytes
Max nice priority 40 40
Max realtime priority 0 0
Max realtime timeout unlimited unlimited us
App Java 内存限制
frameworks/native/build/tablet-10in-xhdpi-2048-dalvik-heap.mk
dalvik.vm.heapsize : 该参数设置VM所能使用的最大的heap的内存大小。即一个Android应用程序在开启largeHeap后,所能使用到的最大的VM的heap的大小,但不会超过该阈值
dalvik.vm.heapgrowthlimit : 该参数用来设置一个Java进程在默认情况下能使用的最大VM的heap的大小
PRODUCT_VENDOR_PROPERTIES += \
dalvik.vm.heapstartsize?=16m \
dalvik.vm.heapgrowthlimit?=256m \
dalvik.vm.heapsize?=768m \
dalvik.vm.heaptargetutilization?=0.75 \
dalvik.vm.heapminfree?=512k \
dalvik.vm.heapmaxfree?=8m
内存水位
可用内存 (KB) | 转换后内存 (MB) | 杀死大于UID的进程 | 说明 |
---|---|---|---|
18432 | 72 | 0 | 开始考虑杀死进程,但优先级最高的进程不太可能被杀死。 |
23040 | 90 | 100 | 考虑杀死一些后台进程,优先考虑低优先级进程。 |
27648 | 108 | 200 | 更积极地杀死后台进程,以释放内存。 |
32256 | 126 | 250 | 更倾向于杀死多个后台进程。 |
55296 | 216 | 900 | 采取强措施,杀死更多进程,以缓解内存压力。 |
80640 | 315 | 950 | 残余内存非常紧张,几乎毫不犹豫地杀死进程。 |
内存不足案例日志
lowmemorykiller: Kill 'com.android.launcher3' (5130), uid 10041, oom_adj 100 to free 35700kB; reason: low watermark is breached and swap is low (0kB < 48716kB)