OpenHarmony应用的ArkTS内存上限
结论
OpenHarmony应用的ArkTS内存上限是多少?先说结论:
-
4.x系统
计算公式:物理内存 / 9,最小不小于128M
-
5.x系统
计算公式:物理内存 * 0.6,最大不大于1536M
如物理内存约为1944M,在4.x系统上,应用的ArkTS内存上限约为216M,5.x的系统上则约为1169M。接下来我们来看一下内存上限在代码中是如何定义的。
代码
ArkTS内存相关代码均在ets_runtime仓,最大内存相关代码则在ets_runtime下的ecmascript/mem/mem_map_allocator.cpp,码云仓库地址:https://gitee.com/openharmony/arkcompiler_ets_runtime
4.1 Release
void MemMapAllocator::AdapterSuitablePoolCapacity()
{
size_t physicalSize = PhysicalSize();
capacity_ = std::max<size_t>(physicalSize / PHY_SIZE_MULTIPLE, MIN_MEM_POOL_CAPACITY);
if (capacity_ > LARGE_POOL_SIZE) {
capacity_ = std::max<size_t>(capacity_, STANDARD_POOL_SIZE);
} else if (capacity_ >= MEDIUM_POOL_SIZE) {
capacity_ = std::min<size_t>(capacity_, STANDARD_POOL_SIZE);
} else if (capacity_ >= LOW_POOL_SIZE) {
capacity_ = std::max<size_t>(capacity_, 128_MB);
}
LOG_GC(INFO) << "Ark Auto adapter memory pool capacity:" << capacity_;
}
- physicalSize:物理内存,可以通过
cat /proc/meminfo
中的MemTotal
字段查看 - PHY_SIZE_MULTIPLE:WORKER_NUM + 1,WORKER_NUM为常量8,代表着一个应用可以有8个Worker线程。PHY_SIZE_MULTIPLE最终值为9
- MIN_MEM_POOL_CAPACITY:64_MB
- LARGE_POOL_SIZE:480_MB
- MEDIUM_POOL_SIZE:256_MB
- LOW_POOL_SIZE:64_MB
- STANDARD_POOL_SIZE:WORKER_NUM * DEFAULT_WORKER_HEAP_SIZE + DEFAULT_HEAP_SIZE,这个值计算后比较大,为6592_MB(这么设计的原因暂且未知)
应用最大ArkTS内存使用 物理内存 / 9,这里记做计算值:
- 如果这个计算值大于480,则取计算值与6592两者的最大值
- 如果在256到480之间,则取计算值与6592两者的最小值,通过计算得知这里一般取计算值
- 如果在64到256之间,则取计算值与128两者的最大值
即当物理内存非常大时,应用内存也会非常大且可能是一个固定值(6592),当物理内存在4G内时,则按除以9算,但最小不会小于128M。
5.0.2 Release
void MemMapAllocator::AdapterSuitablePoolCapacity()
{
size_t physicalSize = PhysicalSize();
capacity_ = std::min<size_t>(physicalSize * DEFAULT_CAPACITY_RATE, MAX_MEM_POOL_CAPACITY);
LOG_GC(INFO) << "Ark Auto adapter memory pool capacity:" << capacity_;
}
- DEFAULT_CAPACITY_RATE:固定值0.6
- MAX_MEM_POOL_CAPACITY:固定值1536_MB
在5.x版本的系统上,应用最大ArkTS内存计算则简单许多,为 物理内存 * 0.6,且最大不会超过1536M
AdapterSuitablePoolCapacity函数调用时机
应用最大ArkTS内存通过capacity_
变量来控制,capacity_
则通过AdapterSuitablePoolCapacity函数计算得来。那么AdapterSuitablePoolCapacity是在何时被调用,又是如何通过capacity_
控制内存分配的?
以下代码基于5.0.2
创建EcmaVM时:
...
Runtime::CreateIfFirstVm(options);
...
CreateIfFirstVm函数
LockHolder lock(*vmCreationLock_);
if (!firstVmCreated_) {
...
MemMapAllocator::GetInstance()->Initialize(ecmascript::DEFAULT_REGION_SIZE);
...
firstVmCreated_ = true;
}
MemMapAllocator的Initialize函数
void Initialize(size_t alignment)
{
AdapterSuitablePoolCapacity();
memMapTotalSize_ = 0;
InitializeHugeRegionMap(alignment);
InitializeRegularRegionMap(alignment);
}
AdapterSuitablePoolCapacity的调用逻辑比较简单,基本就是创建EcmaVM时就会调用。其他子系统通过EcmaVM来与JavaScript运行时交互。
接下来看看capacity_
,首先应用申请ArkTS内存会走到HeapRegionAllocator::AllocateAlignedRegion函数:
Region *HeapRegionAllocator::AllocateAlignedRegion(Space *space, size_t capacity, JSThread* thread, BaseHeap *heap,
bool isFresh)
{
....
auto pool = MemMapAllocator::GetInstance()->Allocate(tid, capacity, DEFAULT_REGION_SIZE,
ToSpaceTypeName(space->GetSpaceType()), isRegular, isMachineCode, Jit::GetInstance()->IsEnableJitFort(), shouldPageTag);
void *mapMem = pool.GetMem();
if (mapMem == nullptr) {
if (thread != nullptr && thread->GetEcmaVM()->IsInitialized()) {
...
heap->ThrowOutOfMemoryErrorForDefault(thread, DEFAULT_REGION_SIZE,
"HeapRegionAllocator::AllocateAlignedRegion", false);
}
LOG_ECMA_MEM(FATAL) << "pool is empty " << annoMemoryUsage_.load(std::memory_order_relaxed);
UNREACHABLE();
}
...
return region;
}
可以看到,该函数内部会调用MemMapAllocator::Allocate来申请内存,如果申请的内存为空,则会抛出OutOfMemory异常。
MemMap MemMapAllocator::Allocate(const uint32_t threadId, size_t size, size_t alignment,
const std::string &spaceName, bool regular, bool isMachineCode, bool isEnableJitFort,
bool shouldPageTag)
{
MemMap mem;
PageTagType type = isMachineCode ? PageTagType::MACHINE_CODE : PageTagType::HEAP;
if (regular) {
mem = memMapPool_.GetRegularMemFromCommitted(size);
if (mem.GetMem() != nullptr) {
...
return mem;
}
if (UNLIKELY(memMapTotalSize_ + size > capacity_)) {
LOG_GC(ERROR) << "memory map overflow";
return MemMap();
}
mem = memMapPool_.GetMemFromCache(size);
if (mem.GetMem() != nullptr) {
memMapTotalSize_ += size;
...
return mem;
}
mem = PageMap(REGULAR_REGION_MMAP_SIZE, PAGE_PROT_NONE, alignment);
memMapPool_.InsertMemMap(mem);
mem = memMapPool_.SplitMemFromCache(mem);
} else {
if (UNLIKELY(memMapTotalSize_ + size > capacity_)) {
LOG_GC(ERROR) << "memory map overflow";
return MemMap();
}
...
}
if (mem.GetMem() != nullptr) {
...
memMapTotalSize_ += mem.GetSize();
}
return mem;
}
申请内存的逻辑:
-
首先查看保留内存(Committed,待释放的内存优先进入此区域,直至此区域满载)中是否能获取内存,如果能则申请成功
-
如果memMapTotalSize_加上当前待申请的内存大小,大于
capacity_
,就会返回一个空的MemMap,从而导致抛出抛出OutOfMemory异常 -
如果还未超出上限,尝试从cache中复用内存(被释放的内存),并增加memMapTotalSize_
-
如果cache中无可用内存,则申请一块新内存并增加memMapTotalSize_
查看系统计算后具体的值
通过抓取应用启动时的日志,查找其中的Ark Auto adapter memory pool capacity
相关日志即可查看
查看应用ArkTS内存占用
通过hidumper --mem pid
指令可以查看应用的ArkTS内存占用,可以查看其中ark ts heap一项,如:
# hidumper --mem 1025
-------------------------------[memory]-------------------------------
Pss Shared Shared Private Private Swap SwapPss Heap Heap Heap
Total Clean Dirty Clean Dirty Total Total Size Alloc Free
( kB ) ( kB ) ( kB ) ( kB ) ( kB ) ( kB ) ( kB ) ( kB ) ( kB ) ( kB )
------------------------------------------------------------------------------------------------------------------------------------------------------
GL 7038 0 0 0 7038 0 0 0 0 0
Graph 16624 0 0 0 16624 0 0 0 0 0
ark ts heap 88562 4380 0 88380 0 32 32 0 0 0
guard 0 0 0 0 0 0 0 0 0 0
native heap 127400 21924 0 126472 0 1764 1764 145812 130355 3452
AnonPage other 5980 3440 0 5728 0 260 260 0 0 0
stack 1688 0 0 1688 0 0 0 0 0 0
.db 152 24 0 140 0 0 0 0 0 0
.so 40833 112224 14744 18860 4552 0 0 0 0 0
.ttf 4632 4052 0 3168 0 0 0 0 0 0
dev 8 0 316 0 0 0 0 0 0 0
FilePage other 54176 23620 7448 36544 5428 0 0 0 0 0
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
Total 349149 169664 22508 280980 33642 2056 2056 145812 130355 3452
native heap:
jemalloc meta: 4370 248 0 4360 0 304 304 0 0 0
jemalloc heap: 120932 17756 0 120128 0 1460 1460 0 0 0
brk heap: 2066 3920 0 1952 0 0 0 0 0 0
musl heap: 32 0 0 32 0 0 0 0 0 0
Purgeable:
PurgSum:0 kB
PurgPin:0 kB
DMA:
Dma:16624 kB
上述示例的应用,ArkTS内存占用了88562K,即大约86M。该示例运行在5.0.2的系统上,其他版本系统字段可能会有差异。
OOM
当应用出现闪退,但是没有faultlog时,可以查看日志中是否有类似下面的日志:
ArkCompiler: [gc] ThrowOutOfMemoryErrorForDefault:xxx OutOfMemory when trying to allocate 262144 bytes function name: HeapRegionAllocator::AllocateAlignedRegion
如果有则说明出现了OutOfMemory错误,解决办法有两个:
- 审视应用使用的内存是否合理,如果不合理,则需要减少应用申请的内存
- 如果应用内存使用合理,则说明系统给的最大内存限制可能过小,可以参考上面的代码逻辑来定制化修改上限
更多推荐
所有评论(0)