关于虚拟机参数的调整 --- heapgrowthlimit/heapsize的配置

1. dalvik.vm.heapgrowthlimit和dalvik.vm.heapsize是什么

heapgrowthlimit/heapsize是虚拟机用量的约束,如下面Dalvik的用量会受到这个限制

adb shell dumpsys meminfo $(adb shell pidof com.android.systemui)
Applications Memory Usage (in Kilobytes):
Uptime: 6856858 Realtime: 6856858

** MEMINFO in pid 15255 [com.android.systemui] **
                   Pss  Private  Private  SwapPss      Rss     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty    Total     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------   ------
  Native Heap     2505     2484        0    12094     2696    16664    15365     1298
  Dalvik Heap     8867     8840        0     2210     8992    16496     8248     8248
 Dalvik Other     1620     1500        0     1260     2060                           
        Stack      452      452        0      524      452                           
       Ashmem        0        0        0        0       16                           
    Other dev       32        0        8        0     1448                           
     .so mmap     1696       96      792      202    16012                           
    .jar mmap     1719        0      632        0    19528                           
    .apk mmap     1943        0     1344        0    16724                           
    .ttf mmap      324        0      324        0      324                           
    .dex mmap     3514       20     3492       16     3532                           
    .oat mmap      945        0      252        0    11476                           
    .art mmap     2949     2368        8      934     7548                           
   Other mmap      139        4       72        0     1324                           
   EGL mtrack     2015     2015        0        0     2015                           
    GL mtrack     7140     7140        0        0     7140                           
      Unknown      220      220        0      400      260                           
        TOTAL    53720    25139     6924    17640    53720    33160    23613     9546

目前基本上使用Android原生配置的这个值,很少应用实际用量会由于超过这个值的约束,一般都是由于gl/egl导致应用实体内存用量过大的问题。

除了实体用量约束,还有虚拟地址用量的约束,虚拟地址用量是这个值的2倍

=>
dalvik.vm.heapgrowthlimit 常规app使用的参数
dalvik.vm.heapsize 应用自己在AndroidManifest.xml设置了android:largeHeap="true",将会变成大应用的设置

如大应用虚拟内存virtual sizedalvik.vm.heapsize的2倍,常规应用virtual sizedalvik.vm.heapgrowthlimit的2倍

$ adb shell getprop | grep "dalvik.vm.heap"
[dalvik.vm.heapgrowthlimit]: [128m]
[dalvik.vm.heapsize]: [256m]

adb shell showmap $(adb shell pidof system_server) | grep "dalvik-main"
 virtual                     shared   shared  private  private
    size      RSS      PSS    clean    dirty    clean    dirty     swap  swapPSS   #  object
  524288     8196     8196        0        0        0     8196    17884    17884    1 [anon:dalvik-main space (region space)]

adb shell showmap $(adb shell pidof com.android.vending) | grep "dalvik-main"
 virtual                     shared   shared  private  private
    size      RSS      PSS    clean    dirty    clean    dirty     swap  swapPSS   #  object
  262144     3776     3776        0        0        0     3776        4        4    1 [anon:dalvik-main space (region space)]

2. android:largeHeap如何影响初始设置

在包解析parseBaseApplication的时候,会设置ApplicationInfo.FLAG_LARGE_HEAP区别普通应用

//frameworks/base/core/java/android/content/pm/PackageParser.java
private boolean parseBaseApplication(Package owner, Resources res,
    //...
    if (sa.getBoolean(
            com.android.internal.R.styleable.AndroidManifestApplication_largeHeap,
            false)) {
        ai.flags |= ApplicationInfo.FLAG_LARGE_HEAP;
    }

接下去在进程启动handleBindApplication的时候,会判断这个值,分别调用clearGrowthLimit(大应用调用) clampGrowthLimit(常规应用调用)

//frameworks/base/core/java/android/app/ActivityThread.java
private void handleBindApplication(AppBindData data) {
//...
    if ((data.appInfo.flags&ApplicationInfo.FLAG_LARGE_HEAP) != 0) {
        dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();
    } else {
        // Small heap, clamp to the current growth limit and let the heap release
        // pages after the growth limit to the non growth limit capacity.
        dalvik.system.VMRuntime.getRuntime().clampGrowthLimit();
    }

于是就会影响虚拟机初始设置,至于clearGrowthLimitclampGrowthLimit这2个方法,后面一起讲解

3. 遇到过需要动这个参数的问题

之前遇到过项目中system_server出现native error(NE),原因是Out of memory,当时查看system_server实体用量其实不大,但是为什么这个项目比较容易遇到Out of memory呢,后面调查发现,其实是虚拟内存用量爆掉了。

VmPeak表示进程所占用最大虚拟内存大小,VmSize表示进程当前虚拟内存大小

VmPeak:3012615 kB
VmSize:2614240 kB

可以看到system_server进程最大的虚拟内存用量都到3G了,一般32bit的设备分配给虚拟内存的地址空间最大是3GCPU的地址总线的是32位的,可寻址范围2^32(4G),高1G的空间为内核空间,低3G的空间为用户空间);64bit的设备一般就不会出现这种问题,虚拟地址空间充裕得很。问题主要集中在32bit的设备上。

既然找到问题了,那么我们看一下map里面的地址是否如此

 virtual                     shared   shared  private  private
    size      RSS      PSS    clean    dirty    clean    dirty     swap  swapPSS   #  object
-------- -------- -------- -------- -------- -------- -------- -------- -------- ---- ------------------------------
1048576     7808     7808        0        0        0     7808        0        0    5 [anon:dalvik-main space (region space)]

该项目dalvik虚拟地址空间已经到了1G(当然就算是1G,也不应该爆掉,这里面还跟别的虚拟用量有关系,这里仅仅是针对anon:dalvik-main space (region space))

继续查看dalvik的属性

[dalvik.vm.heapgrowthlimit]: [256m]
[dalvik.vm.heapsize]: [512m]

发现heapsize确实比较大,512m

针对anon:dalvik-main space (region space))这类较大的一般都建议在32bit上进行调整,不然留给app申请其它虚拟内存的范围就很少了

如调整减半

[dalvik.vm.heapgrowthlimit]: [128m]
[dalvik.vm.heapsize]: [256m]

如下anon:dalvik-main space (region space))就只有512m的占用了,初始消耗虚拟内存是VmSize是 1.53G左右,可以有较充裕的空间给到其它功能。32bit上系统虚拟内存,开机建议控制在1.4-1.7G之间

 virtual                     shared   shared  private  private
    size      RSS      PSS    clean    dirty    clean    dirty     swap  swapPSS   #  object
-------- -------- -------- -------- -------- -------- -------- -------- -------- ---- ------------------------------
 1537736   200828    75456   125264    17688    37652    20224    14876    14876 3235 TOTAL

  524288     7060     7060        0        0        0     7060        0        0   13 [anon:dalvik-main space (region space)]

4. 从问题入手,我们接着探究一下这个是怎么影响到region space的

1、在AndroidRuntime启动虚拟机的时候,会根据系统属性,传入虚拟机,如dalvik.vm.heapsize传入的标识是-Xmxdalvik.vm.heapgrowthlimit是-XX:HeapGrowthLimit=

frameworks/base/core/jni/AndroidRuntime.cpp
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool primary_zygote) {
//...
    parseRuntimeOption("dalvik.vm.heapsize", heapsizeOptsBuf, "-Xmx", "16m");
    parseRuntimeOption("dalvik.vm.heapgrowthlimit", heapgrowthlimitOptsBuf, "-XX:HeapGrowthLimit=");

2、在art的代码parsed_options.cc(解析虚拟机参数的地方)直接搜索刚才的-XmxXX:HeapGrowthLimit=就能得到虚拟机对应的名字

//art/runtime/parsed_options.cc
.Define("-Xmx_")
    .WithType<MemoryKiB>()
    .IntoKey(M::MemoryMaximumSize) //对应dalvik.vm.heapsize

.Define("-XX:HeapGrowthLimit=_")
    .WithType<MemoryKiB>()
    .IntoKey(M::HeapGrowthLimit)  //对应dalvik.vm.heapgrowthlimit

3、在runtime.cc中这些参数都是用来构建虚拟机的gc::Heap

//art/runtime/runtime.cc
bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
//...
  heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize),
                       runtime_options.GetOrDefault(Opt::HeapGrowthLimit), //dalvik.vm.heapgrowthlimit, HeapGrowthLimit是第二个参数
                       runtime_options.GetOrDefault(Opt::HeapMinFree),
                       runtime_options.GetOrDefault(Opt::HeapMaxFree),
                       runtime_options.GetOrDefault(Opt::HeapTargetUtilization),
                       foreground_heap_growth_multiplier,
                       runtime_options.GetOrDefault(Opt::StopForNativeAllocs),
                       runtime_options.GetOrDefault(Opt::MemoryMaximumSize), //dalvik.vm.heapsize, MemoryMaximumSize是第8个参数
                       runtime_options.GetOrDefault(Opt::NonMovingSpaceCapacity),
                       GetBootClassPath(),
                       GetBootClassPathLocations(),
                       image_location_,
                       instruction_set_,
                       // Override the collector type to CC if the read barrier config.
                       kUseReadBarrier ? gc::kCollectorTypeCC : xgc_option.collector_type_,    //gc type,注意此处android R使用的是kCollectorTypeCC
                       kUseReadBarrier ? BackgroundGcOption(gc::kCollectorTypeCCBackground)
                                       : runtime_options.GetOrDefault(Opt::BackgroundGc),
                       /* ... */

4、heap.cc里面会用到这些值,如growth_limit_初始值就是dalvik.vm.heapgrowthlimitcapacity_初始值就是dalvik.vm.heapsize,同时在构造函数里面可以看到RegionSpace默认创建就是按照2倍的capacity_来创建的MemMap region_space_mem_map = space::RegionSpace::CreateMemMap(kRegionSpaceName, capacity_ * 2, request_begin); ,好的现在知道默认虚拟机大小是2倍dalvik.vm.heapsize

//art/runtime/gc/heap.cc
Heap::Heap(size_t initial_size,
           size_t growth_limit,//dalvik.vm.heapgrowthlimit
           size_t min_free,
           size_t max_free,
           double target_utilization,
           double foreground_heap_growth_multiplier,
           size_t stop_for_native_allocs,
           size_t capacity,//dalvik.vm.heapsize
           size_t non_moving_space_capacity,
           const std::vector<std::string>& boot_class_path,
           const std::vector<std::string>& boot_class_path_locations,
           const std::string& image_file_name,
           const InstructionSet image_instruction_set,
           CollectorType foreground_collector_type,
           CollectorType background_collector_type,
           /* ... */)
    : /* ... */
      foreground_collector_type_(foreground_collector_type),
      background_collector_type_(background_collector_type),
      /* ... */
      capacity_(capacity),
      growth_limit_(growth_limit),
      /* ... */

  if (foreground_collector_type_ == kCollectorTypeCC) {
    MemMap region_space_mem_map =
        space::RegionSpace::CreateMemMap(kRegionSpaceName, capacity_ * 2, request_begin);

5、直接在第2章节里面已经讲过handleBindApplication的时候,会分别调用clearGrowthLimit(大应用调用) clampGrowthLimit(常规应用调用),那逐一来看一下

=> clearGrowthLimit,将growth_limit_设置成capacity_都用dalvik.vm.heapsize,同时调用malloc_space->ClearGrowthLimit清除growth_limit_ 之前的设置,重新设定为GetMemMap()->Size()

//art/runtime/gc/heap.cc
void Heap::ClearGrowthLimit() {
  if (target_footprint_.load(std::memory_order_relaxed) == growth_limit_
      && growth_limit_ < capacity_) {
    target_footprint_.store(capacity_, std::memory_order_relaxed);
    concurrent_start_bytes_ =
        UnsignedDifference(capacity_, kMinConcurrentRemainingBytes);
  }
  growth_limit_ = capacity_;
  ScopedObjectAccess soa(Thread::Current());
  for (const auto& space : continuous_spaces_) {
    if (space->IsMallocSpace()) {
      gc::space::MallocSpace* malloc_space = space->AsMallocSpace();
      malloc_space->ClearGrowthLimit();
      malloc_space->SetFootprintLimit(malloc_space->Capacity());
    }
  }
  // This space isn't added for performance reasons.
  if (main_space_backup_.get() != nullptr) {
    main_space_backup_->ClearGrowthLimit();
    main_space_backup_->SetFootprintLimit(main_space_backup_->Capacity());
  }
}
//art/runtime/gc/space/malloc_space.h
  // Removes the fork time growth limit on capacity, allowing the application to allocate up to the
  // maximum reserved size of the heap.
  void ClearGrowthLimit() {
    growth_limit_ = NonGrowthLimitCapacity();
  }
  // The total amount of memory reserved for the alloc space.
  size_t NonGrowthLimitCapacity() const override {
    return GetMemMap()->Size();
  }

=> clampGrowthLimit中会将capacity_设置成growth_limit_,也就是都变成dalvik.vm.heapgrowthlimit的值,同时这里会更改region_space_的大小region_space_->ClampGrowthLimit(2 * capacity_);dalvik.vm.heapgrowthlimit2倍

void Heap::ClampGrowthLimit() {
  // Use heap bitmap lock to guard against races with BindLiveToMarkBitmap.
  ScopedObjectAccess soa(Thread::Current());
  WriterMutexLock mu(soa.Self(), *Locks::heap_bitmap_lock_);
  capacity_ = growth_limit_;
  for (const auto& space : continuous_spaces_) {
    if (space->IsMallocSpace()) {
      gc::space::MallocSpace* malloc_space = space->AsMallocSpace();
      malloc_space->ClampGrowthLimit();
    }
  }
  if (collector_type_ == kCollectorTypeCC) {
    DCHECK(region_space_ != nullptr);
    // Twice the capacity as CC needs extra space for evacuating objects.
    region_space_->ClampGrowthLimit(2 * capacity_);
  }
  // This space isn't added for performance reasons.
  if (main_space_backup_.get() != nullptr) {
    main_space_backup_->ClampGrowthLimit();
  }
}

region_spaceClampGrowthLimit会重新设定GetMemMap的size

//art/runtime/gc/space/region_space.cc
void RegionSpace::ClampGrowthLimit(size_t new_capacity) {
  MutexLock mu(Thread::Current(), region_lock_);
  CHECK_LE(new_capacity, NonGrowthLimitCapacity());
  size_t new_num_regions = new_capacity / kRegionSize;
  if (non_free_region_index_limit_ > new_num_regions) {
    LOG(WARNING) << "Couldn't clamp region space as there are regions in use beyond growth limit.";
    return;
  }
  num_regions_ = new_num_regions;
  if (kCyclicRegionAllocation && cyclic_alloc_region_index_ >= num_regions_) {
    cyclic_alloc_region_index_ = 0u;
  }
  SetLimit(Begin() + new_capacity);
  if (Size() > new_capacity) {
    SetEnd(Limit());
  }
  GetMarkBitmap()->SetHeapSize(new_capacity);
  GetMemMap()->SetSize(new_capacity);
}

阿里的有个代码也主要是为了在app中可以调用ClampGrowthLimit,来避免阿里系的应用由于设置了largeHeap导致的阿里系oom频繁的问题(32bit系统中),为啥这个大了会导致oom,具体可以查看章节3

至此这2个参数应该已经解释的比较清楚了,后面的文章会讲解其它虚拟机参数如何配置,和它的作用

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值