Android ART 虛擬機簡析

源碼基于:Android U

1. prop

名稱選項名稱heap 變量名稱功能

dalvik.vm.heapstartsize

MemoryInitialSize

initial_heap_size_

虛擬機在啟動時,向系統申請的起始內存

dalvik.vm.heapgrowthlimit

HeapGrowthLimit

growth_limit_

應用可使用的 max heap,超過這個值就會產生 OOM

dalvik.vm.heapsize

MemoryMaximumSize

capacity_

特殊應用的內存最大值,需要在應用manifest.xml 中設定: android:largeHeap="true" 此時,變量growth_limit_ 被重置為capacity_ 另外,對于CC 收集器RegionSpace 的內存是 capacity_ * 2

dalvik.vm.foreground-heap-growth-multiplier

ForegroundHeapGrowthMultiplier

foreground_heap_growth_multiplier_

low memry模式下,且沒有定義該prop 時,該值為 1.0f 如果定義了該prop,最終在prop值的基礎上 +1.0f

dalvik.vm.heapminfree

HeapMinFree

min_free_

單次堆內存調整的最小值,也是管理內存需要的最小空閑內存

dalvik.vm.heapmaxfree

HeapMaxFree

max_free_

單次堆內存調整的最大值,也是管理內存需要的最大空閑內存

dalvik.vm.heaptargetutilization

HeapTargetUtilization

target_utilization_

堆目標利用率

首先,對比 dalvik.vm.heapsize 和 dalvik.vm.heapgrowthlimit 兩個屬性,在 ActivityThread.java 中handleBindApplication() 函數中會根據 android:largeHeap 屬性確定使用哪個值為 heap 最大值:

        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. b/18387825dalvik.system.VMRuntime.getRuntime().clampGrowthLimit();}

后面四個值用來確保每次GC 之后,java 堆已經使用和空閑的內存有一個合適的比例,這樣可以盡量地減少GC 的次數。

調整的方式按照如下的規則:

art/runtime/gc/heap.ccvoid Heap::GrowForUtilization(collector::GarbageCollector* collector_ran,size_t bytes_allocated_before_gc) {...if (gc_type != collector::kGcTypeSticky) {// Grow the heap for non sticky GC.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_;grow_bytes = std::min(delta, static_cast<uint64_t>(max_free_));grow_bytes = std::max(grow_bytes, static_cast<uint64_t>(min_free_));target_size = bytes_allocated + static_cast<uint64_t>(grow_bytes * multiplier);next_gc_type_ = collector::kGcTypeSticky;}if (!ignore_target_footprint_) {SetIdealFootprint(target_size);...}

按照上面公式,假設 live_size=120M,target_utilization=0.75,max_free=8M,min_free=2M,那么:

delta =120M * (1/0.75 - 1)=40M

delta(40M) > max_free(8M)

target_size=120M + 8M = 128M,即堆的尺寸在此次 GC 之后調整到128M。

這個target_size 對應下文第 2.4 節中的 total_memory 值。對應的log 如下:

system_server: NativeAlloc concurrent copying GC freed 405107(20MB) AllocSpace objects, 238(4760KB) LOS objects, 33% free, 46MB/70MB, paused 83us,119us total 245.909mssystem_server: NativeAlloc concurrent copying GC freed 330013(15MB) AllocSpace objects, 67(1340KB) LOS objects, 33% free, 47MB/71MB, paused 94us,276us total 214.045mssystem_server: Background concurrent copying GC freed 302867(18MB) AllocSpace objects, 565(11MB) LOS objects, 32% free, 48MB/72MB, paused 588us,176us total 346.099mssystem_server: NativeAlloc concurrent copying GC freed 365830(18MB) AllocSpace objects, 254(5080KB) LOS objects, 33% free, 48MB/72MB, paused 143us,133us total 306.945mssystem_server: Background concurrent copying GC freed 235782(15MB) AllocSpace objects, 666(13MB) LOS objects, 33% free, 47MB/71MB, paused 83us,127us total 160.238ms

上面計算的 target_size 是下次申請內存時是否需要 GC 的一個重要指標,下面結合場景理解。

場景一:

target_size=128M,live_size=120M,如果此時需要分配一個1M 內存對象?

管理的內存最大為8M,當請求分配 1M 內存是,可用內存 8 - 1 = 7M > min_free,所以此次申請無需GC,也不用調整 target_size。

場景二:

target_size=128M,live_size=120M,如果此時需要分配一個7M 內存對象?

管理的內存最大為8M,當請求分配 7M 內存是,可用內存 8 - 7 = 1M < min_free,所以此次申請需要GC,且調整 target_size。

場景三:

target_size=128M,live_size=120M,如果此時需要分配一個10M 內存對象?

管理的內存最大為8M,當請求分配 10M 內存是,已經超過了 8M 空間,先GC 并調整target_size,再次請求分配,如果還是失敗,將 target_size 調整為最大,再次請求分配,失敗就再 GC一次軟引用,再次請求,還是失敗那就是OOM,成功后要調整 target_size。

所以,Android在申請內存的時候,可能先分配,也可能先GC,也可能不GC,這里面最關鍵的點就是內存利用率跟Free內存的上下限。

2. GC log

system_server: Background concurrent copying GC freed 82590(11MB) AllocSpace objects, 1139(22MB) LOS objects, 31% free, 52MB/76MB, paused 326us,284us total 302.779ms.mobile.service: Background young concurrent copying GC freed 185026(5214KB) AllocSpace objects, 60(7396KB) LOS objects, 59% free, 7072KB/17MB, paused 26.144ms,21us total 40.78

這里以 system_server 的 GC log 為例。

2.1 字段Background

這里展示是觸發 GC 的原因,所有 GC 的原因都被記錄在 gc_cause.h 中:

art/runtime/gc/gc_cause.henum GcCause {kGcCauseNone,kGcCauseForAlloc,kGcCauseBackground,kGcCauseExplicit,kGcCauseForNativeAlloc,...
};
indexname備注

kGcCauseNone

None

無效類型,用于占位

kGcCauseForAlloc

Alloc

分配失敗時觸發GC,分配會被block,直到GC 完成 通過new 分配新對象時,如果heap size 超過max,需要先GC

kGcCauseBackground

Background

后臺GC 這里的“后臺”并不是指應用切到后臺才會執行的GC,而是GC在運行時基本不會影響其他線程的執行,所以也可以理解為并發GC。在每一次成功分配Java對象后,都會去檢測是否需要進行下一次GC,這就是GcCauseBackground GC的觸發時機。觸發的條件需要滿足一個判斷,如果new_num_bytes_allocated(所有已分配的字節數,包括此次新分配的對象) >= concurrent_start_bytes_(下一次GC觸發的閾值),那么就請求一次新的GC。

kGcCauseExplicit

Explicit

顯示調用 System.gc() 觸發的 GC

kGcCauseForNativeAlloc

NativeAlloc

當native 分配,出現內存緊張時觸發GC 需要確認 native + java 的權重是否超過了總的heap size

2.2 字段 concurrent

這里展示的是 GC 的收集器名稱,代表不同 GC 算法。所有GC 收集器定義在 collector_type.h 中:

art/runtime/gc/collector_type.henum CollectorType {kCollectorTypeNone,kCollectorTypeMS,kCollectorTypeCMS,kCollectorTypeCMC,kCollectorTypeSS,kCollectorTypeHeapTrim,kCollectorTypeCC,...
};
index收集器名稱功能

kCollectorTypeMS

mark sweep

標記清除算法由標記階段和清除階段構成。 mark 階段是把所有活動對象都做上標記,sweep 階段是把那些沒有標記的對象也就是非活動的對象進行回收的過程。通過這兩個階段,可以使用不用利用的內存空間重新得到利用。

kCollectorTypeCMS

concurrent mark sweep

并發的MS

kCollectorTypeCMC

concurrent mark compact

標記整理算法是將標記清除算法和復制算法相結合的產物。標記整理算法由標記階段和壓縮階段構成。 mark 階段是把所有活動對象都做上標記,compact 階段通過數次搜索堆來重新裝填活動對象。因 compact 而產生的優點是不用犧牲半個堆。

kCollectorTypeSS

semispace

綜合了semi-space和mark-sweep,同時還支持compact

kCollectorTypeCC

concurrent copying

是對mark sweep 而導致內存碎片化的一個解決方案。 算法利用From 空間進行分配。當From 空間被完全占滿時,GC 會將活動對象全部復制到To 空間。當復制完成后,該算法會把From 空間和To 空間互換,GC 也就結束了。From 空間和To 空間大小必須一致。這是為了保證能把From 空間中的所有活動對象都收納到To 空間里。

2.3 GC freed 字段

GC freed 會統計兩個數據:

  • AllocSpace objects,這里展示的是此次 GC 回收的非 LOS 的字節數;

  • LOS objects,這里展示的是此次 GC 回收的 LOS 的字節數;

2.4 31% free, 52MB/76MB 字段

這里展示當前進程在此次 GC 之后的內存情況。

這里記錄已經分配的總內存(記作 current_heap_size)已經分配內存的最大值 (記作 total_memory,這個值不會超過最大值)。

52 M 就是 current_heap_size,76M 就是total_memory。

free 百分比 = (total_memory - current_heap_size) / total_memory

2.5 paused 字段

這里展示當前進程在此次GC 中應用掛起的時間以及次數。

每次掛起的時間都會打印出來,中間用逗號分隔。

2.6 total 字段

這里展示當前進程在此次 GC 中完成所需要的時間,其中包括 paused 時間。

2.7 源碼

源碼于 art/runtime/gc/heap.cc

 art/runtime/gc/heap.ccvoid Heap::LogGC(GcCause gc_cause, collector::GarbageCollector* collector) {...LOG(INFO) << gc_cause << " " << collector->GetName()<< (is_sampled ? " (sampled)" : "")<< " GC freed "  << current_gc_iteration_.GetFreedObjects() << "("<< PrettySize(current_gc_iteration_.GetFreedBytes()) << ") AllocSpace objects, "<< current_gc_iteration_.GetFreedLargeObjects() << "("<< PrettySize(current_gc_iteration_.GetFreedLargeObjectBytes()) << ") LOS objects, "<< percent_free << "% free, " << PrettySize(current_heap_size) << "/"<< PrettySize(total_memory) << ", " << "paused " << pause_string.str()<< " total " << PrettyDuration((duration / 1000) * 1000);...
}

3. 收集機制簡介

Heap 類提供了三種GC 接口:

  • CollectGarbage(),用來執行顯示GC,例如 system.gc() 接口;

  • ConcurrentGC(),用來執行并行GC,只能被 ART 運行時內部的GC 守護線程調用;

  • CollectGarbageInternal(),ART運行時內部調用的GC 接口,可以執行各種類型的GC;

ART runtime 將空間劃分:Image Space、Malloc Space、Zygote Space、Bump Pointer Space、Region Space、Large Object Space。

art/runtime/gc/space/space.henum SpaceType {kSpaceTypeImageSpace,kSpaceTypeMallocSpace,kSpaceTypeZygoteSpace,kSpaceTypeBumpPointerSpace,kSpaceTypeLargeObjectSpace,kSpaceTypeRegionSpace,
};

其中前面都是在地址空間上連續的,即 Continuous Space,而 Large Object Space 是一些離散地址的集合,用來分配一些大對象,稱為Discontinuous Space。

原先Davlik虛擬機使用的是傳統的 dlmalloc 內存分配器進行內存分配。這個內存分配器是Linux上很常用的,但是它沒有為多線程環境做過優化,因此Google為ART虛擬機開發了一個新的內存分配器:RoSalloc,它的全稱是Rows of Slots allocator。RoSalloc相較于dlmalloc來說,在多線程環境下有更好的支持:在dlmalloc中,分配內存時使用了全局的內存鎖,這就很容易造成性能不佳。而在RoSalloc中,允許在線程本地區域存儲小對象,這就是避免了全局鎖的等待時間。ART虛擬機中,這兩種內存分配器都有使用。

Heap 類的 Heap::AllocObject是為對象分配內存的入口,如下:

art/runtime/gc/heap.htemplate <bool kInstrumented = true, typename PreFenceVisitor>mirror::Object* AllocObject(Thread* self,ObjPtr<mirror::Class> klass,size_t num_bytes,const PreFenceVisitor& pre_fence_visitor)REQUIRES_SHARED(Locks::mutator_lock_)REQUIRES(!*gc_complete_lock_,!*pending_task_lock_,!*backtrace_lock_,!process_state_update_lock_,!Roles::uninterruptible_) {//AllocObjectWithAllocator() 實現在 heap-inl.h 中return AllocObjectWithAllocator<kInstrumented>(self,klass,num_bytes,GetCurrentAllocator(),pre_fence_visitor);}

會首先通過?Heap::TryToAllocate嘗試進行內存的分配。在?Heap::TryToAllocate方法,會根據AllocatorType,選擇不同的Space進行內存的分配。在?Heap::TryToAllocate 方法失敗時,會調用 Heap::AllocateInternalWithGc 進行 GC,然后在嘗試內存的分配。

參考:

https://developer.aliyun.com/article/652546#slide-5

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/15556.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/15556.shtml
英文地址,請注明出處:http://en.pswp.cn/web/15556.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Scikit-Learn樸素貝葉斯

Scikit-Learn樸素貝葉斯 1、樸素貝葉斯1.1、貝葉斯分類1.2、貝葉斯定理1.3、貝葉斯定理的推導1.4、樸素貝葉斯及原理1.5、樸素貝葉斯的優缺點2、Scikit-Learn樸素貝葉斯2.1、Sklearn中的貝葉斯分類器2.2、Scikit-Learn樸素貝葉斯API2.3、Scikit-Learn樸素貝葉斯實踐(新聞分類與…

Python——文件操作相關

1. 讀文件方式 第一種 有規律的名稱 第二種 無規律的名稱 2. 文件名稱 用 “{:05d}” 來規范輸出數字所占位數&#xff0c;例如&#xff1a; for i in range(100):gt_file Reference/ image "{:05d}".format(i) .jpgprint(gt_file)輸出&#xff1a; ... Re…

爬山算法的詳細介紹

目錄 &#x1f349;概述 &#x1f349; 步驟 &#x1f349; 優缺點 &#x1f348;優點 &#x1f348;缺點 &#x1f348;應對策略 &#x1f349;示例 &#x1f348;旅行商問題 &#x1f34d;步驟 &#x1f34d;分解代碼 &#x1f34e;包含頭文件 &#x1f34e;定義函…

Cortex-M3的SysTick 定時器

目錄 概述 1 SysTick 定時器 1.1 SysTick 定時器功能介紹 1.2 SysTick 定時器功能實現 1.3 SysTick在系統中的作用 2 SysTick應用的實例 2.1 建立異常服務例程 2.2 使能異常 2.3 鬧鐘功能 2.4 重定位向量表 2.5 消滅二次觸發 3 SysTick在FreeRTOS中的應用 3.1 STM…

【代碼】結構體

哈嘍大家好&#xff0c;我是學霸小羊&#xff0c;今天講講結構體。 先看例題&#xff1a; 例1.老師給了小楊一份同學們的考試成績&#xff0c;包括語數英三科&#xff0c;老師讓小明按照總分排序&#xff0c;請你幫幫他吧&#xff01; 輸入數據&#xff1a; 第1行 學生總人…

在docker中運行SLAM十四講程序

《十四講》的示例程序依賴比較多&#xff0c;而且系統有點舊。可以在容器中運行。 拉取鏡像 docker pull ddhogan/slambook:v0.1這個docker對應的github&#xff1a;HomeLH/slambook2-docker 拉下來之后&#xff0c;假如是Windows系統&#xff0c;需要使用XLaunch用于提供X11…

面試大雜燴之kafka

面試這個領域最近環境不行&#xff0c;所以卷起來流量挺大 關于K8s 其實看我之前的博客&#xff0c;k8s剛有點苗頭的時候我就研究過&#xff0c;然后工作的時候間接接觸 也自己玩過 但是用的不多就忘記了&#xff0c;正苦于不知道寫什么&#xff0c;水一篇 用來面試應該是夠了…

C++ | Leetcode C++題解之第111題二叉樹的最小深度

題目&#xff1a; 題解&#xff1a; class Solution { public:int minDepth(TreeNode *root) {if (root nullptr) {return 0;}queue<pair<TreeNode *, int> > que;que.emplace(root, 1);while (!que.empty()) {TreeNode *node que.front().first;int depth que…

VC編譯sample_onnx_mnist提示無法打開輸入文件cudnn.lib

出現錯誤 LNK1181 無法打開輸入文件“C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.1\lib\x64\cudnn.lib” 解決辦法&#xff1a;下載cudnn&#xff0c;NVIDIA cuDNN | NVIDIA Developer 拷貝相應的文件到CUDA安裝的目錄下。 VC編譯libtorch提示無法打開輸入文件cu…

huggingface 筆記:PretrainModel

1 from_pretrained 從預訓練模型配置中實例化一個 PyTorch 預訓練模型默認情況下&#xff0c;模型使用 model.eval() 設置為評估模式&#xff08;Dropout 模塊被禁用&#xff09; 要訓練模型&#xff0c;應該首先使用 model.train() 將其設置回訓練模式 1.1 主要參數 pretra…

java 子類繼承父類

為什么需要繼承 我現在要有兩個類一個 一個是小學生&#xff0c;一個是大學生 代碼 小學生 package b; public class encapsulatio{public String name;public int age;public double score;public void setscore (double score) {this.scorescore;}public void testing() {S…

(三)MySQL 索引

歡迎訪問 什么是索引&#xff1f; 提高查詢效率的一種數據結構&#xff0c;索引是數據的目錄 索引的分類 按「數據結構」分類&#xff1a;Btree索引、Hash索引、Full-text索引。按「物理存儲」分類&#xff1a;聚簇索引、二級索引。按「字段特性」分類&#xff1a;主鍵索引…

Spring6 對 集成MyBatis 開發運用(附有詳細的操作步驟)

詳細實現操作步驟 具體實現內容&#xff1a;我們運用 Spring6 和 MyBatis 實現一個轉賬操作(該轉賬操作&#xff0c;進行一個事務上的控制&#xff0c;運用 MyBatis 執行 SQL 語句)。 第一步&#xff1a;準備數據庫表 使用t_act表&#xff08;賬戶表&#xff09; 連接數據庫的…

三個有意思的鏈表面試題的完成

上一篇博客我們已經完成了鏈表的所有內容&#xff0c;那么這一篇博客我們來看一下三個特別有意思的鏈表題目。 **第一個題目如下&#xff1a;**相信不少朋友看到這題目就已經暈了&#xff0c;那就簡單說明下這個題目&#xff0c;題目就是創建一個鏈表&#xff0c;其中每個節點…

深入探索Java中的流式編程:優雅地處理集合數據

Java流式編程&#xff08;Stream API&#xff09;是Java 8引入的一項重要特性&#xff0c;它為處理集合數據提供了一種更為優雅和函數式的方式。通過流式操作&#xff0c;開發者可以以更簡潔、更直觀的方式處理數據&#xff0c;從而提高代碼的可讀性和可維護性。本文將深入探討…

Android14 - 繪制系統 - 概覽

從Android 12開始&#xff0c;Android的繪制系統有結構性變化&#xff0c; 在繪制的生產消費者模式中&#xff0c;新增BLASTBufferQueue&#xff0c;客戶端進程自行進行queue的生產和消費&#xff0c;隨后通過Transation提交到SurfaceFlinger&#xff0c;如此可以使得各進程將緩…

【vue3+elementuiplus】el-select下拉框會自動觸發校驗規則

場景&#xff1a;編輯彈框省份字段下拉框必填&#xff0c;觸發方式change&#xff0c;有值第一次打開不會觸發校驗提示&#xff0c;關閉彈框再次打開觸發必填校驗提示&#xff0c;但是該字段有值 問題的原因是&#xff1a;在關閉彈層事件中&#xff0c;我做了resetfileds&…

SpringBoot + MybatisPlus

SpringBoot MybatisPlus 整合記錄 1. 硬件軟件基本信息2. 相關鏈接3. 通過idea快速生成一個Springboot項目4. 啟動報錯問題解決問題一&#xff1a;Springboot啟動的時候報錯提示 “沒有符合條件的Bean關于Mapper類型”問題二&#xff1a;啟動的時候提示需要一個Bean&#xff0…

電磁仿真--CST網格介紹

1. 簡介 網格會影響仿真的準確性和速度&#xff0c;花時間理解網格化過程是很重要的。 CST 中可用的數值方法包括FIT、TLM、FEM、MoM&#xff0c;使用不同類型的網格&#xff1a; FIT和TLM&#xff1a;六面體 FEM&#xff1a;四面體、平面 MoM&#xff1a;表面 CFD&#…

深入理解與防御跨站腳本攻擊(XSS):從搭建實驗環境到實戰演練的全面教程

跨站腳本攻擊&#xff08;XSS&#xff09;是一種常見的網絡攻擊手段&#xff0c;它允許攻擊者在受害者的瀏覽器中執行惡意腳本。以下是一個XSS攻擊的實操教程&#xff0c;包括搭建實驗環境、編寫測試程序代碼、挖掘和攻擊XSS漏洞的步驟。 搭建實驗環境 1. 安裝DVWA&#xff…