关于虚拟机的参数的调整 --- heaptargetutilization/heapmaxfree/heapminfree/heapstartsize/foreground-heap-growth-multiplier的配置

  • 1. 这些参数都是什么意思
  • 2. ART解析使用这些参数
  • 3. 案例讲解
  • 4. ART参数调试时加入的日志

1. 这些参数都是什么意思

[dalvik.vm.foreground-heap-growth-multiplier]: [2.0]
=>
标记的是前后台进程分配内存用量的时候增长空间的比例,如果设置了2.0,代表前台分配空间会是3倍的增长。

建议配置值:2.0 (前后台申请内存用量分开,前台是后台的3倍)
如果是特别低内存的版本如512M,可以不设置这个值,如果不存在这个值,在低内存设备默认前台也会是1倍的增长(相当于值设置了 0 )

[dalvik.vm.heaptargetutilization]: [0.5]
=>
Android Q或者更小的android版本默认值是0.5, android R/S已经将这个值默认设置成0.75
具体可以查看art/runtime/gc/heap.hkDefaultTargetUtilization

值越大,每次增长堆的量越小,堆的利用率越大
如果是0.5, 则每次前台增长空间量大约是 已分配空间的 3倍, 后台为 1倍
如果是 0.75, 则每次前台增长空间量大约是 已分配空间的 1倍, 后台为 1/3倍

=>这个值改成0.75可以降低process的memory用量,该值影响的是zygote进程的初始用量(如某个项目,修改前是6m,修改后是3m),
对于每个进程的最终用量也会有优化(影响的是最后memory分配后稳定的值的范围)

建议配置值:0.75,当然如果你内存比较充裕,设置成0.5也没有问题

[dalvik.vm.heapmaxfree]: [8m]
=>
heap 的max_free 就是用来设置max_free_
用来设置单次堆内存调整的最大值。 注:这里还需要 * multiplier, 即 后台为 max_free_, 前台为max_free_ * 3(这里写的是heaptargetutilization是0.5的情况)

这个可以明显减少gc次数,为调整前system_server gc次数是21次/com.android.systemui是18次,调整后system_server gc次数是16次/com.android.systemui是6次

默认art中没有配置是2M, 请实际测试开机是各个进程gc此次,
还有如taobao/weixin启动过程中gc次数再来确定这个值(需要兼顾实体memory用量大小)

如下是建议配置值: 1G-2G RAM : 2m/3m 3G-4G RAM : 4mheapstartsize 4G-6G RAM : 8m
8G RAM以上请实际测试为准

[dalvik.vm.heapminfree]: [512k]
=>
对应min_free_ ,设置单次增长最小值。也是一样要* multiplier ,同时前后也不一样会根据heaptargetutilization 调整
和上面的heapmaxfree 是一个逻辑

在低RAM的设备上(如2G或者以下)不建议调整这个值,在高RAM设备上(如6G或者以上)可以适当调整这个值,
这个值会影响很多小内存进程的内存用量,太大了会对内存压力影响较大

建议配置值: 512K,内存充裕可以设置成2m/4m/8m

[dalvik.vm.heapstartsize]: [2m]
=>
art默认设置成2m
具体可以查看art/runtime/gc/heap.hstatic constexpr size_t kDefaultInitialSize = 2 * MB;

在此基础上,把startsize改为8M 50M都对 max_allowed_footprint_(现在叫target_footprint_)的初始值没有大的影响
因为在 PreZygoteFork 时会对 Zygote 做一次GC,这时 Zygote 会调整这个 footprint
后面进程都是从Zygote fork出来的,所以新进程的值主要依赖于上面这个 Zygote 的值
当然 Zygote 的值是变动的,新fork出来的进程值也是变动的

建议配置值:关注zygote大小和gc次数再确定,可以根据设备内存大小适当增大这个值,如5m/8m/16m都是可以配置的

2. ART解析使用这些参数

这些参数,传入都是在虚拟机初始化的时候,注意里面的注释,如dalvik.vm.heapstartsize,传入虚拟机中的是-Xms

//frameworks/base/core/jni/AndroidRuntime.cpp
parseRuntimeOption("dalvik.vm.heapstartsize", heapstartsizeOptsBuf, "-Xms", "4m");//传入的是-Xms
parseRuntimeOption("dalvik.vm.heapminfree", heapminfreeOptsBuf, "-XX:HeapMinFree=");//传入的是-XX:HeapMinFree
parseRuntimeOption("dalvik.vm.heapmaxfree", heapmaxfreeOptsBuf, "-XX:HeapMaxFree=");//传入的是-XX:HeapMaxFree
parseRuntimeOption("dalvik.vm.heaptargetutilization",heaptargetutilizationOptsBuf,"-XX:HeapTargetUtilization=");//传入的是-XX:HeapTargetUtilization
/* Foreground heap growth multiplier option */
parseRuntimeOption("dalvik.vm.foreground-heap-growth-multiplier",foregroundHeapGrowthMultiplierOptsBuf,"-XX:ForegroundHeapGrowthMultiplier=");//传入的是-XX:ForegroundHeapGrowthMultiplier

后面art解析参数是在art/runtime/parsed_options.cc,使用其构建heap是在art/runtime/runtime.cc和之前介绍heapgrowthlimit/heapsize流程是一样的。

dalvik.vm.foreground-heap-growth-multiplier需要特别提一下,关于章节1里面讲的“前后台”内存申请的区别具体代码在下面,最终用来构建前台内存申请的倍数foreground_heap_growth_multiplier

//art/runtime/gc/heap.h
static constexpr double kDefaultHeapGrowthMultiplier = 2.0;//art/runtime/runtime.cc
static constexpr double kExtraDefaultHeapGrowthMultiplier = kUseReadBarrier ? 1.0 : 0.0;//默认这个是1.0float foreground_heap_growth_multiplier;//低内存没有设置dalvik.vm.foreground-heap-growth-multiplier默认是1.0,如果设置了就跑else的逻辑if (is_low_memory_mode_ && !runtime_options.Exists(Opt::ForegroundHeapGrowthMultiplier)) {// If low memory mode, use 1.0 as the multiplier by default.foreground_heap_growth_multiplier = 1.0f;} else {//其它情况没有设置默认是dalvik.vm.foreground-heap-growth-multiplier + 1 = 3;(ForegroundHeapGrowthMultiplier没设置是2)foreground_heap_growth_multiplier =runtime_options.GetOrDefault(Opt::ForegroundHeapGrowthMultiplier) +kExtraDefaultHeapGrowthMultiplier;}

构建heap的时候,这几个属性值都对应如下参数

Heap::Heap(size_t initial_size, //dalvik.vm.heapstartsizesize_t min_free, //dalvik.vm.heapminfreesize_t max_free, //dalvik.vm.heapmaxfreedouble target_utilization,//heaptargetutilizationdouble foreground_heap_growth_multiplier,//heaptargetutilization:target_footprint_(initial_size),min_free_(min_free),max_free_(max_free),target_utilization_(target_utilization),foreground_heap_growth_multiplier_(foreground_heap_growth_multiplier),

后面我们直接来讲一下其中比较关键的一个函数GrowForUtilization,如CollectGarbageInternal->GrowForUtilizationCollectGarbageInternal会先回收gc,然后再调用GrowForUtilization进行内存增长,然后gc才会结束

void Heap::GrowForUtilization(collector::GarbageCollector* collector_ran,size_t bytes_allocated_before_gc) {//内存增长的时候调用// We know what our utilization is at this moment.// This doesn't actually resize any memory. It just lets the heap grow more when necessary.const size_t bytes_allocated = GetBytesAllocated();//bytes_allocated 需要分配的内存用量// Trace the new heap size after the GC is finished.TraceHeapSize(bytes_allocated);//trace这个应用uint64_t target_size, grow_bytes;collector::GcType gc_type = collector_ran->GetGcType();MutexLock mu(Thread::Current(), process_state_update_lock_);// Use the multiplier to grow more for foreground.const double multiplier = HeapGrowthMultiplier();  //返回需要增长的内存用量倍数,上面已经讲过,前后台比例会根据dalvik.vm.foreground-heap-growth-multiplier的设置也改变if (gc_type != collector::kGcTypeSticky) {//一般都走进这里来// Grow the heap for non sticky GC.// GetTargetHeapUtilization获取的是target_utilization_,也就是dalvik.vm.heaptargetutilization// 如果dalvik.vm.heaptargetutilization设置0.5, 那么bytes_allocated/0.5 - bytes_allocated = bytes_allocated,就是被分配的内存,1倍的增长量// 如果dalvik.vm.heaptargetutilization设置0.75,那么bytes_allocated/0.75 - bytes_allocated = 1/3 * bytes_allocated,1/3的增长量// delta 你可以先认为是当前准备增长的内存用量uint64_t delta = bytes_allocated * (1.0 / GetTargetHeapUtilization() - 1.0);DCHECK_LE(delta, std::numeric_limits<size_t>::max()) << "bytes_allocated=" << bytes_allocated<< " target_utilization_=" << target_utilization_;// 准备增长的内存用量delta,和max_free_(dalvik.vm.heapmaxfree)的最小值,最大不能超过max_free_grow_bytes = std::min(delta, static_cast<uint64_t>(max_free_));// 准备增长的内存用量grow_bytes,和min_free_(dalvik.vm.heapminfree)的最大值,最小不会小于min_free_grow_bytes = std::max(grow_bytes, static_cast<uint64_t>(min_free_));// 最终增加的内存用量是grow_bytes * multiplier(上面说的前后台增长比例,一般前台是3.0,后台是1.0)// 最后得出内存用量目标的大小target_sizetarget_size = bytes_allocated + static_cast<uint64_t>(grow_bytes * multiplier);next_gc_type_ = collector::kGcTypeSticky;

=> GetBytesAllocated获取的是当前内存的用量

  // Number of bytes currently allocated and not yet reclaimed. Includes active// TLABS in their entirety, even if they have not yet been parceled out.Atomic<size_t> num_bytes_allocated_;// Returns the number of bytes currently allocated.// The result should be treated as an approximation, if it is being concurrently updated.size_t GetBytesAllocated() const {return num_bytes_allocated_.load(std::memory_order_relaxed);}

到这里基本上虚拟机内存分配的逻辑讲的差不多了。后面会讲一个虚拟机分配,通俗的例子

3. 案例讲解

1.org (heaptargetutilization 0.5, heapmaxfree=2m, heapminfree=512k):

(前台增长空间量大约是 增加已分配空间的 3倍, 后台为 1倍)

=> 如上一次gc之后,需要1M,那么前台设置4M的基准值,后台2M基准值

前台增量给约束到1.5m-6m之间(heapminfree的3倍是1.5m,heapmaxfree的3倍是6m):也就是最终值(下次到达后触发gc的值)是4m

后台增量给约束到512K-2m之间:也就是最终值(下次到达后触发gc的值)是2m

=> 如上一次gc之后,需要10M,那么前台设置40M的基准值,后台20M基准值

前台增量给约束到1.5m-6m之间:也就是最终值是16m

后台增量给约束到512K-2m之间,也就是最终值是12m

=>(前台进程2M、后台进程2M以下的进程都是基准值进行调整,2M已经是增加6m/2m了)

2.0.75设置之后(heaptargetutilization 0.75, heapmaxfree=2m, heapminfree=512k):

0.75设置之后,会修改基准值:前台增长空间量大约是 已分配空间的 1倍, 后台为 1/3倍

=> 如上一次gc之后,需要1M,那么前台设置2M的基准值,后台1.3M基准值

前台增量给约束到1.5m-6m之间:也就是最终值是2.5m //最低增长1.5M

后台增量给约束到512K-2m之间:也就是最终值是1.5m //最低增长0.5M

=> 如上一次gc之后,需要10M,那么前台设置20M的基准值,后台13.3M基准值

前台增量给约束到1.5m-6m之间:也就是最终值是16m

后台增量给约束到512K-2m之间,也就是最终值是12m

ps: 修改对于小应用有影响,大应用影响不大,因为大应用都是基于在大的heapmaxfree在确定下次gc的值。 小进程gc的频率可能增加(前台进程6M、后台进程6M以下的进程),不过考虑到最终小应用的内存用量会减少,而且小进程的堆小的话gc时间也会较短(影响较小)。

3.0.75+heapmaxfree=3m(heaptargetutilization 0.75, heapmaxfree=3m, heapminfree=512k)

0.75设置之后,会修改基准值:前台增长空间量大约是 已分配空间的 1倍, 后台为 1/3倍

=> 如上一次gc之后,需要1M,那么前台设置2M的基准值,后台1.3M基准值

前台增量给约束到1.5m-9m之间:也就是最终值是2.5m

后台增量给约束到512K-3m之间:也就是最终值是1.5m

=> 如上一次gc之后,需要10M,那么前台设置20M的基准值,后台13.3M基准值

前台增量给约束到1.5m-9m之间:也就是最终值是19m

后台增量给约束到512K-3m之间,也就是最终值是13m

ps:可以看到heapmaxfree增加可以显著减少大应用gc次数,更快分配到相应的堆大小,对于第三方应用应该有帮助。 小应用和上述一样,跟这个值关系不是很大。这个修改是为了第三方和一些大应用的gc造成的卡顿。

=>通俗的分配内存例子
100变量(假设100的变量中有1半可以给gc,有1半是临时变量用完可以马上回收)
利用率0.75(前台2倍)

50 触发gc :

0/50->分配50->50/50(使用量/上限值)->gc->25/50->(根据利用率调整)->25/50->再次分配25->50(有25的已经回收不能再次回收)/50->gc->37.5/50->(根据利用率调整)->37.5/(37.5*2=75)
->最后再分配25(目前已经分配完了100)->62.5/75 =>最后的值是62.5

30 触发gc:

0/30->分配30->30/30(使用量/上限值)->gc->15/30->(根据利用率调整)->15/30->再次分配15->30(有15的已经回收不能再次回收)/30->gc->22.5/30->(根据利用率调整)->22.5/45
->最后再分配22.5->45(有22.5的已经回收不能再次回收)/45->gc->33.75/45->(根据利用率调整)->33.75/67.5->再次分配32.5(目前已经分配完了100)->66.25/67.5

=> gc此处增加了,最后一次分配的量就是剩余没有gc的数值

4. ART参数调试时加入的日志

具体请参考下面的日志,
1、查看调整后的after1 target_size和最初请求的大小org1 target_size之间的差异。
2、同时也可以查看调整前后system_server 、com.android.systemui、微信、淘宝冷启动时gc的次数
3、还需要system_server 、com.android.systemui、zygote等系统各个进程最终稳定时的内存用量(如果嫌个数多,至少需要查看上面说的3个),如开机5分钟用google脚本测试内存用量值。

void Heap::GrowForUtilization(collector::GarbageCollector* collector_ran,size_t bytes_allocated_before_gc) {//内存增长的时候调用//...grow_bytes = std::max(grow_bytes, static_cast<uint64_t>(min_free_));LOG(INFO) << "yun_hen org1 target_size = " << target_size;target_size = bytes_allocated + static_cast<uint64_t>(grow_bytes * multiplier);LOG(INFO) << "yun_hen after1 target_size = " << target_size;//...if (bytes_allocated + adjusted_max_free < target_footprint) {LOG(INFO) << "yun_hen org2 target_size = " << target_size;target_size = bytes_allocated + adjusted_max_free;LOG(INFO) << "yun_hen after2 target_size = " << target_size;grow_bytes = max_free_;} else {LOG(INFO) << "yun_hen org3 target_size = " << target_size;target_size = std::max(bytes_allocated, target_footprint);LOG(INFO) << "yun_hen after3 target_size = " << target_size;// The same whether jank perceptible or not; just avoid the adjustment.grow_bytes = 0;}//...

=> 同时附上art虚拟机堆栈调试的方法,这个方法对于跟进流程也挺有帮助的

//yun_hen change startstd::string owner_stack_dump;Thread* self = Thread::Current();if (self != nullptr) {// Reacquire mutator_lock_ for logging.ScopedObjectAccess soa(self);Locks::thread_list_lock_->ExclusiveLock(self);struct CollectStackTrace : public Closure {void Run(art::Thread* thread) OVERRIDEREQUIRES_SHARED(art::Locks::mutator_lock_) {thread->DumpJavaStack(oss);}std::ostringstream oss;};CollectStackTrace owner_trace;// RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its// execution.self->RequestSynchronousCheckpoint(&owner_trace);owner_stack_dump = owner_trace.oss.str();LOG(WARNING) << "yun_hen change Current owner stack:\n" << owner_stack_dump<< "......";}//yun_hen change end

heaptargetutilization/heapmaxfree/heapminfree/heapstartsize/multiplier虚拟机参数的配置相关推荐

  1. jdk1.8 Hotspot虚拟机参数通用配置

    恰逢本人最近正在学些JVM虚拟机一些参数配置.简单优化的东西,下面是整理的一些通用参数的含义或配置说明,虽然这些配置网上到处都有,但笔者这是结合实际应用加以整理收集的,希望能带来参考价值,最后附上某一 ...

  2. Java虚拟机参数配置

    虚拟机优化是我们面试常被问到的知识点,也是Java开发运用的重要内容,虽然现在实践中我也尚未接触到足够的内容,并没有在生产环境中进行虚拟机优化的经验,但学习这方面的知识是必不可少的,有备无患对吧! 下 ...

  3. android中dalvik虚拟机参数

    因为要做一个小内存项目,所以提前对虚拟机参数设定查资料,学习设定. 1,dalvik.vm.heapstartsize 堆分配的初始大小,调整这个值会影响到应用的流畅性和整体ram消耗.这个值越小,系 ...

  4. java虚拟机参数-X 与 -XX的区别

    java虚拟机参数-X 与 -XX的区别 java虚拟机参数-X 与 -XX的区别 为什么有的用-X 如,-Xms -Xmx -Xmn等 有的用-XX: 如-XX:MaxPermSize=100M等 ...

  5. jvm_虚拟机参数讲解(二)

    堆里面分Eden区,s0,s1区,老年代,整体分两块大的区,复制算法分别转换角色,对于JAVA里面的栈,里面有局部变量表,操作数栈,帧数据区,主要是操作数栈是一个主要的概念,局部变量表和帧数据区主要是 ...

  6. jvm_虚拟机参数讲解(一)

    其实在JAVA中,虚拟机参数主要起到的作用,在虚拟机运行的时候,在程序运行的时候,这个虚拟机的调配,参数会对应用系统的运行的良好,性能等等都会产生直接的关系,比如说我们有两个方面堆参数的分配大多数的参 ...

  7. 启动rrt什么意思_python学习第144课--创建虚拟机、设置虚拟机参数以及启动虚拟机...

    [每天几分钟,从零入门python编程的世界!] 上节我们介绍了下载虚拟机以及centOS的相关事项,现在我们创建虚拟机. ●创建虚拟机 安装好virtualbox之后,我们点击Oracle VM V ...

  8. java虚拟机参数详解

    Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOME\bin\java –option 来启动,-option为虚拟机参数,JAVA_H ...

  9. eclipse中对于Java虚拟机参数的设置与思考

    1 设置方法 通过直接对编辑jdk进行设置 也可以在在Debug Configration中对特定工程进行设置,如下图: 2 常用设置及其含义 常用设置:1)  -Xmx3550m -Xms3550m ...

最新文章

  1. 【Python】Label组件 Button组件 Checkbutton组件
  2. 应用化学:从二氯甲烷到四氯化碳
  3. Java Web之BaseServlet的抽取
  4. 线性序列机与串行接口ADC驱动设计与验证
  5. Java IdentityHashMap isEmpty()方法与示例
  6. 为什么现在的年轻人越来越不愿意结婚、生子了?
  7. 比较 Cache 和虚拟存储器,说明它们的相似点和不同。
  8. 制作CDKEY:有效期的处理
  9. windows 不安装jdk 运行 jar_详解Windows系统安装运行Mongodb服务(推荐)
  10. 怎样把自己喜欢的微信表情包(动态)导出来,我三岁半的表弟都会...
  11. 【亲测有效】Ubuntu系统开机速度慢解决办法
  12. 电子工程师12个忠告,新手老鸟都值得一看!
  13. 闹中取静:寻觅电商蓝海
  14. Android Ibeacon 算法,iBeacon定位算法
  15. 解决Everything无法搜索移动硬盘文件问题(utool搜索不到也是因为Everything的问题)
  16. win32 打印机任务管理的 node 模块 (3)详解Win32 Spooler API 获取打印机列表及状态
  17. Java练习ArrayList的运用——勇者斗史莱姆
  18. java基础知识面试题(2020年最新版)
  19. 如何用计算机蓝牙发送文件,怎么用蓝牙传文件
  20. Knockout.js 整理

热门文章

  1. 实现对手机联系人列表进行读写操作,并用RecyclerView收缩展开方式展现
  2. 钢筋探测器行业研究及十四五规划分析报告
  3. 我的u3d游戏编程之路
  4. Java多线程复习整理(二)
  5. 【多媒体】多媒体架构
  6. python期货基本面分析_用python对股票期货做时序分析
  7. odbc An unsupported operation was attempted
  8. 当学术沾染名利,约翰伯努利对儿子的嫉恨,影响数学界几十年发展
  9. C++11更新内容(2)--完美转发--默认移动构造/移动赋值--1116
  10. 基于Python3.6配置开发环境