赞
踩
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 size
是dalvik.vm.heapsize
的2倍,常规应用virtual size
是dalvik.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)]
在包解析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();
}
于是就会影响虚拟机初始设置,至于clearGrowthLimit
、clampGrowthLimit
这2个方法,后面一起讲解
之前遇到过项目中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
的设备分配给虚拟内存的地址空间最大是3G
(CPU的地址总线的是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)]
1、在AndroidRuntime
启动虚拟机的时候,会根据系统属性,传入虚拟机,如dalvik.vm.heapsize
传入的标识是-Xmx
,dalvik.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
(解析虚拟机参数的地方)直接搜索刚才的-Xmx
、XX: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.heapgrowthlimit
,capacity_
初始值就是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.heapgrowthlimit
的2倍
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_space
的ClampGrowthLimit
会重新设定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个参数应该已经解释的比较清楚了,后面的文章会讲解其它虚拟机参数如何配置,和它的作用
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。