结论

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;
}

申请内存的逻辑:

  1. 首先查看保留内存(Committed,待释放的内存优先进入此区域,直至此区域满载)中是否能获取内存,如果能则申请成功

  2. 如果memMapTotalSize_加上当前待申请的内存大小,大于capacity_,就会返回一个空的MemMap,从而导致抛出抛出OutOfMemory异常

  3. 如果还未超出上限,尝试从cache中复用内存(被释放的内存),并增加memMapTotalSize_

  4. 如果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错误,解决办法有两个:

  1. 审视应用使用的内存是否合理,如果不合理,则需要减少应用申请的内存
  2. 如果应用内存使用合理,则说明系统给的最大内存限制可能过小,可以参考上面的代码逻辑来定制化修改上限
Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐