Android Dalvik虚拟机 堆初始化流程
前言
上篇文章介绍了dalvik虚拟机启动流程,在dalvik虚拟机启动时调用了dvmGcStartup来启动堆。
本文介绍我们在日常开发使用Java时的堆创建流程。
Dalvik堆介绍
Dalvik虚拟机中,堆是由heap[0] Active堆和heap[1] Zygote堆两部分组成的。其中,Zygote堆用来管理Zygote进程在启动过程中预加载和创建的各种对象,而Active堆是在Zygote进程fork第一个子进程之前创建的。
之后无论是Zygote进程还是其子进程,都在Active堆上进行对象分配和释放。这样做的目的是使得Zygote进程和其子进程最大限度地共享Zygote堆所占用的内存。
Dalvik虚拟机管理中的重要结构包括一个Card Table、两个Heap Bitmap和一个GcMarkStack。
HeapBitmap
HeapBitmap是堆的内存分配情况的映射图,它的每一个bit位记录着堆中每8个字节的分配情况。
堆中有两个HeapBitmap,一个称为LiveHeapBitmap,用来记录上次GC之后还存活的对象;另一个称为MarkHeapBitmap,用来记录当前GC中还存活的对象。这样,上次GC后存活的但是当前GC不存活的对象,就是需要释放的对象。
GcMarkStack
Davlk虚拟机使用标记-清除(Mark-Sweep)算法进行GC。在标记阶段,通过一个Mark Stack来实现递归检查被引用的对象,即在当前GC中存活的对象。有了这个Mark Stack,就可以通过循环来模拟函数递归调用。
在垃圾回收的过程中,需要通过递归的方式去检查系统中的每个对象。但是递归太深会引起栈溢出,因此,实际采用的回收算法中用GcMarkStack来保存中间的数据。
CardTable
Card Table是为了记录在垃圾收集过程中对象的引用情况的,用在Concurrent GC第二阶段记录非垃圾收集堆对象对垃圾收集堆对象的引用。后文会分析内存回收流程,即gc流程。
Card Table和Heap Bitmap的作用是类似的。区别在于:
- Card Table不是使用一个bit来描述一个对象,而是用一个byte来描述GC_CARD_SIZE个对象;
- Card Table不是用来描述对象的存活,而是用来描述在Concurrent GC的过程中被修改的对象,这些对象需要进行特殊处理。
初始化zygote堆
- dalvik/vm/alloc/Alloc.cpp
// Initialize the GC universe.
bool dvmGcStartup()
{dvmInitMutex(&gDvm.gcHeapLock);pthread_cond_init(&gDvm.gcHeapCond, NULL);return dvmHeapStartup();
}
- dalvik/vm/alloc/Heap.cpp
初始化堆,当heapGrowthLimit=0时,使用heapMaximumSize
// Initialize the GC heap.
bool dvmHeapStartup()
{GcHeap *gcHeap;if (gDvm.heapGrowthLimit == 0) {gDvm.heapGrowthLimit = gDvm.heapMaximumSize;}gcHeap = dvmHeapSourceStartup(gDvm.heapStartingSize, gDvm.heapMaximumSize, gDvm.heapGrowthLimit);gDvm.gcHeap = gcHeap;// Set up the lists we'll use for cleared reference objects.gcHeap->clearedReferences = NULL;// 初始化cradTabledvmCardTableStartup(gDvm.heapMaximumSize, gDvm.heapGrowthLimit);return true;
}
- dalvik/vm/alloc/HeapSource.cpp
- dvmAllocRegion()函数来分配一块内存空间,然后把这块内存空间交给dlmalloc来管理;dvmAllocRegion()函数中使用ashmem_create_region()和mmap()函数来分配需要的内存空间,这也意味着dvmAllocRegion()分配的都是大块的内存。以下几个函数中内存分配都是在使用dvmAllocRegion()分配的内存,并没有从Dalvik的堆上分配,因为这几个对象在系统中会一直存在,不能被回收,因此,直接从系统内存中分配,不用Dalvik管理。
- addInitialHeap()函数将创建出来的内存放到了heapSource的字段HeapSource[0]里。Dalvik并没有直接使用系统调用来自己管理动态内存,而是以“私有堆”的形式交给dlmalloc管理。
- dvmHeapBitmapInit()函数创建了两个HeapBitmap的对象,HeapBitmap是堆的内存分配情况的映射图,它的每一个bit位记录着堆中每8个字节的分配情况。
- allocMarkStack()函数分配了一块内存,并用它来初始化GcMarkStack结构。在垃圾回收的过程中,需要通过递归的方式去检查系统中的每个对象。但是递归太深会引起栈溢出,因此,实际采用的回收算法中用GcMarkStack来保存中间的数据。
// Initializes the heap source;
GcHeap* dvmHeapSourceStartup(size_t startSize, size_t maximumSize, size_t growthLimit) {GcHeap *gcHeap;HeapSource *hs;mspace msp;size_t length;void *base;// Allocate a contiguous region of virtual memory to subdivided among the heaps managed by the garbage collector. length = ALIGN_UP_TO_PAGE_SIZE(maximumSize);base = dvmAllocRegion(length, PROT_NONE, gDvm.zygote ? "dalvik-zygote" : "dalvik-heap");// Create an unlocked dlmalloc mspace to use as a heap source.msp = createMspace(base, kInitialMorecoreStart, startSize);gcHeap = (GcHeap *)calloc(1, sizeof(*gcHeap));hs = (HeapSource *)calloc(1, sizeof(*hs));hs->targetUtilization = gDvm.heapTargetUtilization * HEAP_UTILIZATION_MAX;hs->minFree = gDvm.heapMinFree;hs->maxFree = gDvm.heapMaxFree;hs->startSize = startSize;hs->maximumSize = maximumSize;hs->growthLimit = growthLimit;hs->idealSize = startSize;hs->softLimit = SIZE_MAX; // no soft limit at firsths->numHeaps = 0;hs->sawZygote = gDvm.zygote;hs->nativeBytesAllocated = 0;hs->nativeFootprintGCWatermark = startSize;hs->nativeFootprintLimit = startSize * 2;hs->nativeNeedToRunFinalization = false;hs->hasGcThread = false;hs->heapBase = (char *)base;hs->heapLength = length;// Add the initial heap. 初始化heapSource中的第一个堆addInitialHeap(hs, msp, growthLimit);// Initialize a HeapBitmap so that it points to a bitmap large enough to cover a heap at <base> of <maxSize> bytesdvmHeapBitmapInit(&hs->liveBits, base, length, "dalvik-bitmap-1");dvmHeapBitmapInit(&hs->markBits, base, length, "dalvik-bitmap-2");allocMarkStack(&gcHeap->markContext.stack, hs->maximumSize);gcHeap->markContext.bitmap = &hs->markBits;gcHeap->heapSource = hs;gHs = hs;return gcHeap;
}//Add the initial heap.
static bool addInitialHeap(HeapSource *hs, mspace msp, size_t maximumSize)
{if (hs->numHeaps != 0) {return false;}hs->heaps[0].msp = msp;hs->heaps[0].maximumSize = maximumSize;hs->heaps[0].concurrentStartBytes = SIZE_MAX;hs->heaps[0].base = hs->heapBase;hs->heaps[0].limit = hs->heapBase + maximumSize;hs->heaps[0].brk = hs->heapBase + kInitialMorecoreStart;hs->numHeaps = 1;return true;
}// Initialize a HeapBitmap so that it points to a bitmap large enough to cover a heap at <base> of <maxSize> bytes, where objects are guaranteed to be HB_OBJECT_ALIGNMENT-aligned.
bool dvmHeapBitmapInit(HeapBitmap *hb, const void *base, size_t maxSize, const char *name) {void *bits;size_t bitsLen;bitsLen = HB_OFFSET_TO_INDEX(maxSize) * sizeof(*hb->bits);bits = dvmAllocRegion(bitsLen, PROT_READ | PROT_WRITE, name);if (bits == NULL) {ALOGE("Could not mmap %zd-byte ashmem region '%s'", bitsLen, name);return false;}hb->bits = (unsigned long *)bits;hb->bitsLen = hb->allocLen = bitsLen;hb->base = (uintptr_t)base;hb->max = hb->base - 1;return true;
}
初始化active堆
- 直到dvmHeapStartup()函数结束,heapSource中的两个“堆”只有heaps[0]初始化了,heaps[1]仍然为NULL。因为dvmHeapStartup()的调用是在Zygote进程中进行的。
- 在第一个应用启动前,还会继续完成Dalvik内存模块的初始化工作,但该初始化active heap只会进行一次,由gDvm.newZygoteHeapAllocated布尔变量控制,即Zygote进程只会在fork第一个子进程的时候,才会将Java堆划一分为二来管理;这么设计是因为 We create a heap for all future zygote process allocations, in an attempt to avoid touching pages in the zygote heap。
- 在Zygote的nativeFork()函数中还会调用dvmGcPreZygoteFork()函数,其中会调用函数dvmHeapSourceStartupBeforeFork()去初始化active堆,并把该active堆放到heap数组前面,以后无论是Zygote进程,还是Zygote子进程,需要分配对象时,都在Active堆上进行。这样就可以使得Zygote堆最大限度地在Zygote进程及其子进程中共享。
- dalvik/vm/native/dalvik_system_Zygote.cpp
static void Dalvik_dalvik_system_Zygote_fork(const u4* args, JValue* pResult)
{pid_t pid;dvmGcPreZygoteFork(); // 在fork前分配active堆setSignalHandler();dvmDumpLoaderStats("zygote");pid = fork();RETURN_INT(pid);
}
- dalvik/vm/alloc/Alloc.cpp
// Do any last-minute preparation before we call fork() for the first time.
bool dvmGcPreZygoteFork() {return dvmHeapSourceStartupBeforeFork();
}
- dalvik/vm/alloc/HeapSource.cpp
addNewHeap()函数主要的功能是创建了一个新的堆。
创建的过程是将旧的heaps[0]第一页以后的内存地址空间分给了新的堆,然后对新堆的地址空间在原来地址的基础上重新执行mmap。接下来将heaps[0]指向的堆的尺寸减小为一页大小,最后将heaps[0]和heaps[1]的值交换。
因此,两个堆都创建后,大小和以前还是一样,但是heaps[0]指向了一个新的、未分配内存的堆,而heaps[1]则包含了初始化时创建的内存对象,以后的内存分配都将在heaps[0]中进行。
/** This is called while in zygote mode, right before we fork() for the* first time. We create a heap for all future zygote process allocations,* in an attempt to avoid touching pages in the zygote heap. (This would* probably be unnecessary if we had a compacting GC -- the source of our* troubles is small allocations filling in the gaps from larger ones.)*/
bool dvmHeapSourceStartupBeforeFork()
{HeapSource *hs = gHs; // use a local to avoid the implicit "volatile"if (!gDvm.newZygoteHeapAllocated) {// Ensure heaps are trimmed to minimize footprint pre-fork.trimHeaps();// Create a new heap for post-fork zygote allocations. We only try once, even if it fails.gDvm.newZygoteHeapAllocated = true;return addNewHeap(hs);}return true;
}// Adds an additional heap to the heap source. Returns false if there are too many heaps or insufficient free space to add another heap.
static bool addNewHeap(HeapSource *hs)
{Heap heap;memset(&heap, 0, sizeof(heap));// Heap storage comes from a common virtual memory reservation. The new heap will start on the page after the old heap.char *base = hs->heaps[0].brk;size_t overhead = base - hs->heaps[0].base;size_t morecoreStart = SYSTEM_PAGE_SIZE;heap.maximumSize = hs->growthLimit - overhead;heap.concurrentStartBytes = hs->minFree - CONCURRENT_START;heap.base = base;heap.limit = heap.base + heap.maximumSize;heap.brk = heap.base + morecoreStart;remapNewHeap(hs, &heap);heap.msp = createMspace(base, morecoreStart, hs->minFree);// Don't let the soon-to-be-old heap grow any furtherhs->heaps[0].maximumSize = overhead;hs->heaps[0].limit = base;mspace_set_footprint_limit(hs->heaps[0].msp, overhead);// Put the new heap in the list, at heaps[0]memmove(&hs->heaps[1], &hs->heaps[0], hs->numHeaps * sizeof(hs->heaps[0]));hs->heaps[0] = heap;hs->numHeaps++;return true;
}/** A helper for addNewHeap(). Remap the new heap so that it will have* a separate ashmem region with possibly a different name, etc. In* practice, this is used to give the app heap a separate ashmem* region from the zygote heap's.*/
static bool remapNewHeap(HeapSource* hs, Heap* newHeap)
{char* newHeapBase = newHeap->base;size_t rem_size = hs->heapBase + hs->heapLength - newHeapBase;munmap(newHeapBase, rem_size);int fd = ashmem_create_region("dalvik-heap", rem_size);void* addr = mmap(newHeapBase, rem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);int ret = close(fd);return true;
}
参考:Dalvik虚拟机Java堆创建过程分析
相关文章:
Android Dalvik虚拟机 堆初始化流程
前言 上篇文章介绍了dalvik虚拟机启动流程,在dalvik虚拟机启动时调用了dvmGcStartup来启动堆。 本文介绍我们在日常开发使用Java时的堆创建流程。 Dalvik堆介绍 Dalvik虚拟机中,堆是由heap[0] Active堆和heap[1] Zygote堆两部分组成的。其中ÿ…...
0讲(补)——开发前必备基本常识
前言 专栏内容持续补充更新,目前正在进行优惠活动 目录 前言 一、函数的声明和定义 二、预编译 三、串口打印中的printf函数的使用...
JS学习笔记
1.WebAPIs简介导读Web APIs 和JS 基础关联性JS 基础阶段以及 Web APIs 阶段JS基础学习 ECMAScript 基础语法为后面作铺垫,Web APIs 是JS 的应用,大量使用JS基础语法做交互效果①JS 基础阶段我们学习的是ECMAScript 标准规定的基本语法要求同学们掌握JS 基…...
linux005之用户、组管理
linux用户管理简介: 任何使用linux系统的用户,都必须使用一个合法的账号和密码,账号和密码一般都是超级管理员创建,当然普通用户也可以创建用户,前提是必须拥有创建用户权限。 root是linux系统中默认创建的超级用户 创…...
列线图工具_Nomogram
定义 列线图是一种相对传统的分析方法,用于展示自变量和因变量的线性关系,及其特征的重要程度。 现在用SHAP,和机器学习库中的 Feature importance 工具可以实现类似甚至更好效果。不过很多传统的研究领域比较认这种方法。 列线图工具建立在…...
【C++】类和对象(一)
目录一、面向过程和面向对象初步认识二、类的引入三、类的定义四、类的访问限定符及封装4.1、访问限定符4.2、封装五、类的作用域六、类的实例化七、类对象的大小八、this指针8.1、this指针的引出8.2、this指针的特性8.3、C语言和C实现Stack的对比一、面向过程和面向对象初步认…...
Python获取搜索引擎结果
前言 想快速获取各个高校的博士招生网站,于是通过python先获取出有可能包含高校博士招生网站的URL,然后通过人为筛选得到了想要的招生网站(注意,并非直接爬取,是间接获取的)。 整理了一份网站名单&#x…...
2.4.8 PCIe——物理逻辑层——REFCLK
一、概述 pcie的参考时钟由板级输入,提供给IP内PHY层的PLL使用,由PLL产生core_clk和pipe_clk。 二、REFCLK产生方式 Serdes 所用时钟由 PHY 模块内的PLL生成,PLL的参考时钟可以由common clock(外部背板提供)、separ…...
树莓派4B arm64 搭建 docker+drone+gitea
树莓派4B arm64 搭建 dockerdronegitea 记录时间: 2023年02月10日 树莓派烧录 如何用树莓派搭建一台永久运行的个人服务器? https://mp.weixin.qq.com/s?__bizMzI5NjA0ODkwNA&mid2651847658&idx1&sn267a1257b43d4a76f2a081ed157b77f9&chksmf7b11…...
Java的JDBC编程
目录 1. 打开IDEA,新建Project 2. 引入依赖 (1)下载驱动包 (2)将驱动包导入Project 3. 编写代码 (1)创建数据源 (2)让代码和数据库服务器建立联系 (3&…...
CSS:块格式化上下文(BFC)
块格式化上下文是块级盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。 块格式化上下文(BFC)的创建 满足以下条件将创建块格式化上下文: 根元素()浮动元素(float 值不为 none)绝对定位元素…...
paddle表情识别部署
表情识别模块1.环境部署1.1同样采用fastDeploy库1.2相关模型2.封装成静态库2.1参考[百度Paddle中PP-Mattingv2的部署并将之封装并调用一个C静态库](https://blog.csdn.net/weixin_43564060/article/details/128882099)2.2项目依赖添加2.3生成成功3.test3.1创建emotion_test项目…...
Python-第五天 Python函数
Python-第五天 Python函数一、函数介绍1. 什么事函数二、函数的定义1.函数的定义:2.案例三、函数的参数1.函数的传入参数2.案例升级四、函数的返回值1.什么是返回值2.返回值的语法3.None类型4.None类型的应用场景五、函数说明文档1.函数的说明文档2.在PyCharm中查看…...
【Python学习笔记】28.Python3 错误和异常
前言 作为 Python 初学者,在刚学习 Python 编程时,经常会看到一些报错信息,在前面我们没有提及,这章节我们会专门介绍。 Python3 错误和异常 Python 有两种错误很容易辨认:语法错误和异常。 Python assert…...
SQLServer 迁移到 MySQL 工具对比
我之所以会写这篇对比文章,是因为公司新产品研发真实经历过这个痛苦过程(传统基于 SQL Server开发的C/S 产品转为 MySQL云产品)。首次需要数据转换是测试环节,当时为了快速验证新研发云产品性能与结果准确性(算法类&am…...
分析finebi5.x仪表板组件获取数据过程(数据是数据集或者sql的)
首先仪表板的公共连接类似:http://localhost:37799/webroot/decision/link/Bo6B 当我们访问这个连接时,会来到FineLinkAction的getShareReport方法。 public String getShareReport(HttpServletRequest req, HttpServletResponse res, @FinePathVariable("linkId"…...
设计模式--适配器模式 Adapter Pattern
设计模式--适配器模式 Adapter Pattern适配器模式 Adapter Pattern1.1 基本介绍1.2 工作原理类适配器模式对象适配器模式接口适配器模式小结适配器模式 Adapter Pattern 1.1 基本介绍 (1)适配器模式将某个类的接口转换成为客户端期望的另一个接口表示&…...
PVE虚拟机篇-rest api
rest api官方介绍 Proxmox VE API rest api文档 rest api文档 rest api token 调用pve rest api ,有两种认证方式 Ticket Cookie Ticket Cookie的方式是最为推荐的,获取的方式为,通过post请求,发送用户名和密码到pve的server端获取tok…...
2022-2025学年面向中小学生的白名单全国性竞赛活动清单及官网地址链接
**资料来源:爬虫爬取。** 教育部办公厅 工业和信息化部办公厅关于公布 首批特色化示范性软件学院名单的通知 教育部办公厅 工业和信息化部办公厅关于公布首批特色化示范性软件学院名单的通知 - 中华人民共和国教育部政府门户网站 教育部办公厅关于2022-2025学年面向中小学生…...
Python 高级编程之生成器与协程进阶(五)
文章目录一、概述二、生成器1)生成器和迭代器的区别2)生成器创建方式1、通过生成器函数创建2、通过生成器表达式创建3)生成器表达式4)yield关键字5)生成器函数6)return 和 yield 异同7)yield的使…...
Django框架之视图和URL
视图和URL 站点管理页面做好了, 接下来就要做公共访问的页面了.对于Django的设计框架MVT. 用户在URL中请求的是视图.视图接收请求后进行处理.并将处理的结果返回给请求者.使用视图时需要进行两步操作 1.定义视图2.配置URLconf 1. 定义视图 视图就是一个Python函数,…...
Python 的Tkinter包系列之七:好例子补充2
Python 的Tkinter包系列之七:好例子补充2 英汉字典(使用文本文件记录英语单词和解释)、简单的通信录(使用SQLite数据库记录人员信息) 一、tkinter编写英汉字典 先看效果图: 词典文件是一个文本文件&…...
每日一练-等差数列
等差数列🍀题目描述🌿解题思路🌸Python源码📧Summary📆Date: 2023年2月10日 🎬Author: 小 y 同 学 📃Classify: 蓝桥杯每日一练 🔖Language: Python 🍀题目描述 题意 …...
使用动态参数构建CUDA图
文章目录使用动态参数构建CUDA图使用显式 API 调用构建 CUDA 图使用流捕获构建 CUDA 图组合方法执行结果总结使用动态参数构建CUDA图 自从在 CUDA 10 以来,CUDA Graphs 已被用于各种应用程序。 上图将一组 CUDA 内核和其他 CUDA 操作组合在一起,并使用指…...
在Fortran中调用Python教程
前言Python是机器学习领域不断增长的通用语言。拥有一些非常棒的工具包,比如scikit-learn,tensorflow和pytorch。气候模式通常是使用Fortran实现的。那么我们应该将基于Python的机器学习迁移到Fortran模型中吗?数据科学领域可能会利用HTTP AP…...
04-PS人像磨皮方法
1.高斯模糊磨皮 这种方法的原理就是建立一个将原图高斯模糊后图层, 然后用蒙版加画笔或者历史画笔工具将需要磨皮的地方涂抹出来, 通过图层透明度, 画笔流量等参数来控制磨皮程度 1.新建图层(命名为了高斯模糊磨皮), 混合模式设置为正常, 然后选择高斯模糊, 模糊数值设置到看…...
nginx反向代理+负载均衡上传webshell重难点+apache漏洞
nginx反向代理 nginx 负载均衡 负载均衡的策略 1、轮询:nginx默认就是轮询其权重都默认为1,服务器处理请求的顺序:ABABABABAB… upstream mysvr { server 192.168.137.131; server 192.168.137.136; }2、weight:跟据配置…...
transition组件的使用
<template><button click"flag !flag">切换</button><transition name"fade"><div v-if"flag" class"box"></div></transition> </template><script setup lang"ts"&g…...
多行文本在块元素中垂直居中
单行文本垂直居中对齐 在块元素中,让单行文本居中,可以使用line-height等于块元素的高,即可让该单行文本垂直居中对齐。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><me…...
在 WebAssembly 中使用 C/C++ 和 libbpf 编写 eBPF 程序
作者:于桐,郑昱笙 eBPF(extended Berkeley Packet Filter)是一种高性能的内核虚拟机,可以运行在内核空间中,用来收集系统和网络信息。随着计算机技术的不断发展,eBPF 的功能日益强大,…...
北京如何做网站/福州网站排名
网上找了一堆复制粘贴,一点卵用都没有的文章。 无论怎么操作都是报错如下: ERROR 1396 (HY000): Operation ALTER USER failed for rootlocalhost这里有几个关键命令,最主要就是关闭安全模式 -- 查询一下安全模式开关 show variables like …...
电脑怎样做病毒网站/宁波网站优化公司推荐
✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。 🍎个人主页:Matlab科研工作室 🍊个人信条:格物致知。 更多Matlab仿真内容点击👇 智能优化算法 …...
电子商务网站建设外包服务的企业/seodao cn
我是不会选择去做一个普通的人。如果我能够做到的话,我有权成为一个不寻常的人。我寻找机会,但我不寻求安宁。我不希望在国家的照顾下成为一名有保障的市民,那将被人瞧不起而使我痛苦不堪。我要做有意义的冒险。我要梦想,我要创造…...
网站建设与维护考试题/商务软文写作
故障情景 当我们卸载软件之后可能会遇到任务管理器中启动项显示有Program这种无效项目的情况 无效项目如图所示: 解决方法 解决这个问题,需要我们打开注册表进行一些操作 注册表打开方法:Windows键R开启运行对话框,输入regedit并运…...
vps做vpn svn和网站/杭州谷歌推广
不宕机升级K8S集群 RKE对kubernetes版本有要求,使用下面命令可查看当前RKE工具支持的kubernetes版本 rke config --list-version --all从rke v1.1.0开始,rke支持不宕机升级集群 1.节点要求 集群具有至少 3 个 etcd 节点。 集群具有至少 2 个 controlplane 节点。 集群具有…...
建立企业网站的流程/8个公开大数据网站
1. 将字符串转换成时间,字符串格式为05/16/2015 datetime.datetime.strptime(STRING,"%m/%d/%Y") 2. 将时间转换成字符串:格式如括号内所示: dateText.strftime("%Y-%m-%d") 加一天: d2 d1 datetime.timede…...