Decay timer of Jemalloc on android

在Android7.1平台上的Chromium测试中发现了一个内存相关的问题,涉及jemalloc内存管理器对大块内存的回收机制。当使用全局变量分配并释放大块内存时,内存不会立即释放回系统,而是被持有一定时间。

A strange Problem

android 7.1平台上 Chromium test 出现了一项Fail
https://cs.chromium.org/chromium/src/content/browser/tracing/memory_instrumentation_browsertest.cc?l=74&rcl=633bf05fdb96303ebfa68eada1adee9d76dea16f

这项测试检测app初始的内存信息,分配65M的array(保存在 unique_ptr里),然后在JS里使用一些(4M JS array);然后检测app的内存信息。最后,reset这个unique_ptr;再检测一下。预期的结果是内存使用回到初始状态。

为了便于分析问题,我写了个native 测试程序,定义全局变量 nique_ptr buffer_.

std::unique_ptr<char[]> buffer_;

void PopulateBuffer() {
  const int64_t kAllocSize = 65 * 1024 * 1024;
  buffer_ = std::make_unique<char[]>(kAllocSize);
  buffer_.reset();
}

测试结果看到native heap占用的内存,不会释放。

Applications Memory Usage (in Kilobytes):
Uptime: 179111 Realtime: 179111
                   Pss  Private  Private     Swap     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------
  Native Heap    66628    66628        0        0        0        0        0
  Dalvik Heap        0        0        0        0        0        0        0
        Stack       20       20        0        0                           
    Other dev        0        0        0        0                           
     .so mmap      193      120       12        0                           
   Other mmap       47       20       16        0                           
      Unknown      124      124        0        0                           
        TOTAL    67012    66912       28        0        0        0        0

 App Summary
                       Pss(KB)
                        ------
           Java Heap:        0
         Native Heap:    66628
                Code:      132
               Stack:       20
            Graphics:        0
       Private Other:      160
              System:       72

               TOTAL:    67012      TOTAL SWAP (KB):        0

将buffer_定义成char *类型的全局变量,同样可以看到这种情况。

char* buffer_;
void PopulateBuffer() {
char* buffer_;
  const int64_t kAllocSize = 65 * 1024 * 1024;
  buffer_ = new char[kAllocSize];
  delete[] buffer_;
  buffer_ = nullptr;
}

但是将buffer_定义成局部变量,则不会出现这种问题。

void PopulateBuffer() {
  const int64_t kAllocSize = 65 * 1024 * 1024;
  char* buffer = new char[kAllocSize];
  std::memset(buffer, 1, kAllocSize);
  delete[] buffer;
  buffer = nullptr;
}

or

void PopulateBuffer() {
  const int64_t kAllocSize = 65 * 1024 * 1024;
  std::unique_ptr<char[]> buffer = std::make_unique<char[]>(kAllocSize);
  buffer.reset();
}

这个现象只在android 7.1上存在,在其它的版本里没有。android 5.1以后,jemalloc是libc默认的heap 管理器。

这并不是内存泄露,如果再次申请,内存使用量不会增加。过上一段时间后,这个内存还会返还给系统。但是程序已经明确Free了,内存会被hold住一段时间,在低内存设置上会导致内存的紧张。

Decay timer of Jemalloc

为了搞清楚这个问题,大致看了下jemalloc的代码。

Android 7.1 上Decay timer 被下面这个提交设成了1。

https://android.googlesource.com/platform/external/jemalloc/+/08795324eae5f68d211dc5483746af51203dc661%5E%21/

decay timer会影响large object的释放时间,设成1是为了性能问题,因为设成0时,每次free都会purge。decay timer只在android 7.1上被设成了1。

android 8.1上decay timer又被设成了0。

https://android.googlesource.com/platform/external/jemalloc/+/7d7fbe660d1fe68fa412449d6033177319c7da3e%5E%21/

android O上zygote在fork进程时,会通过mallopt将decay timer设成1。所以android O上Java进程在native heap上的表现是和上面问题一样的。native heap上的大块内存会被hold一段时间。

frameworks/base/core/jni/com_android_internal_os_Zygote.cpp

static void PreApplicationInit() {
  // The child process sets this to indicate it's not the zygote.
  gMallocLeakZygoteChild = 1;

  // Set the jemalloc decay time to 1.
  mallopt(M_DECAY_TIME, 1); 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值