Dalvik虚拟机

server/2024/10/20 10:44:52/

Android运行时堆组成
Anroid运行时堆可以分成2种,一种是ZygoteSpace区,一种是ApplicationSpace

  1. ZygoteSpace 区
  • 初始化时创建:在启动Android系统时,由Zygote进程初始化并创建。
  • 预加载核心类和资源:Zygote进程会加载所有核心类和资源,存储在ZygoteSpace中。
  • 享元模式:ZygoteSpace的设计类似享元模式,通过共享来减少对象的创建次数,从而节省内存。
  • 数据共享:ZygoteSpace中的数据是多个进程共享的。从Zygote进程派生出来的每个新进程都会共享这些预加载的数据。
    ZygoteSpace区存放了哪些类还有资源
  1. 核心系统类
  • Java核心库:如 java.lang.,java.util. 及其他标准的Java核心库。
  • Android框架类:如 android.app.,android.content.,android.view.* 等用于应用开发的基本框架类。
  1. 关键组件和服务
  • Activity及其生命周期:Activity 类及相关生命周期管理类。
  • Content Providers:管理应用程序间数据共享的内容提供者类。
  • Broadcast Receivers:处理广播消息的接收器类。
  • Services:后台服务类及其管理机制。
  1. 常用资源
  • 字符串资源:系统级别常用的字符串和文本资源。
  • Drawable资源:系统默认的可绘制资源,如图标、按钮背景等。
  1. 常用静态库和依赖
  • 基础静态链接库:如C++标准库和其他系统级别的基础静态库。
  • 其他第三方依赖:系统常用的第三方库预加载。
  1. VM(虚拟机)初始化配置
  • Dalvik/ART配置:虚拟机相关的配置信息、优化参数等。

为什么要预加载这些内容?
提升应用启动速度

  • 通过在系统启动时预加载这些常用类和资源,避免了每次应用启动时的加载过程,显著提升了应用启动速度。
    内存共享和节省
  • 使用写时复制技术(Copy-on-Write),多个进程共享同一块内存,只有当某个进程需要修改这些共享内容时,才会复制一份新的副本。这种机制极大地节省了内存开销。
  1. ApplicationSpace 区
  • 应用程序启动时创建:每当启动一个新应用程序时,会为其创建一个新的ApplicationSpace。
  • 动态对象创建:新创建的对象存放在ApplicationSpace中。
  • 数据独立:ApplicationSpace中的数据不是进程间共享的。不同的进程拥有各自独立的ApplicationSpace。
  • 一个进程对应一个ApplicationSpace:每个应用进程有独立的ApplicationSpace,进程内部的数据自成一体,不与其他进程共享。
    图示理解
    ±---------------------------+
    | ZygoteSpace |
    | (共享核心类和资源) |
    ±---------------------------+
    | |
    ±-----------+ ±------------+
    | |
    ±--------v---------+ ±--------v---------+
    | 应用进程1 (App1) | | 应用进程2 (App2) |
    | ±-------------+ | | ±-------------+ |
    | |ApplicationSpace| | | |ApplicationSpace| |
    | | (新创建对象) | | | | (新创建对象) | |
    | ±-------------+ | | ±-------------+ |
    ±-------------------+ ±-------------------+

结论

  • ZygoteSpace:
  • 在系统启动时由Zygote进程创建,用于存储预加载的核心类和资源。
  • 使用享元模式,通过共享减少对象的创建次数,节省内存。
  • 这些数据在多个进程之间共享。
  • ApplicationSpace:
  • 在应用程序启动时创建,用于存放新创建的对象。
  • 数据独立,每个进程有独立的ApplicationSpace。
  • 一个进程对应一个ApplicationSpace,每个应用进程的数据互不干扰、不共享。
    最后总结
  • Android运行时堆确实被划分为ZygoteSpace和ApplicationSpace两部分。
  • ZygoteSpace通过共享预加载的核心类和资源在多个进程中实现内存节省。
  • ApplicationSpace则确保了每个进程的数据独立,不同进程有各自的ApplicationSpace。

毕竟这是个堆,用来存放对象的,所以肯定要涉及到垃圾回收,Android运行时堆用的是标记-清除法(我估计这个是早期版本,现在新的版本还用标记-清除法就效率太低了)
所以里面还存放和GC有关的东西

3.Card Table
用于DVM Concurrent GC,当第一次进行垃圾标记后,记录垃圾信息
4.Heap Bitmap
有2个Heap Bitmap,一个用来记录上次GC回收存活的对象,一个用来存放这次GC回收存活的对象
5.Mark Stack
DVM的运行时堆使用标记-清除算法进行GC,Mark Stack就是在GC的标记阶段使用,它用来遍历存活的对象

最开始没理解Heap Bitmap和Mark Stack
因为用的标记-清除法
在标记的时候,Mark Stack会将在可达性链路上的对象标记为1,表示不应该被回收,将不在可达性链路上的对象标记为0,表示应该被回收的对象,然后Mark Stack存储的是在可达性链路上的对象
然后在清除的时候,会把Heap Bitmap为0的清除掉

然后我再想,那么我们为什么要有Mark Stack?直接Heap Bitmap不可以吗
然后看了一下,
1:就是Heap Bitmap其实默认都是0的,我们将在可达性链路上的对象标记为1之后,存放在Mark Stack里面之后,后面会把Mark Stack里面的对象弹出来然后将Heap Bitmap里面对应的对象设置为1
2:如果我们不使用这个Mark Stack显式堆栈的话,那么我们进行标记的时候,用递归的方式标记,如果链路太长(因为我们每次方法的调用都是一次栈帧的入栈),那么这个虚拟机栈就会装入太多栈帧,它的长度受到了操作系统的限制,如果太多,可能就会导致一个栈的溢出。
但是如果我们选择用这个Mark Stack的话,就可以用迭代的方式而避免用递归
//递归实现
void markObject(Object obj) {
if (obj == null || obj.isMarked()){
return;
}
obj.mark();
for (Object ref : obj.getReferences()) {
markObject(ref);
}
}

//显式堆栈实现
void markObject(Object root) {
Stack markStack = new Stack<>():
markStack.push(root);

while (!markStack.isEmpty()) {Object object object pop();if (obj == null || obj.isMarked()) {continue;}obj.mark();for (Object ref : obj.getReferences()) {if (ref != null && !ref.isMarked()) {markStack.push(ref);}}}
}

DVM的GC日志
DVM中每次垃圾收集都会将GC日志打印到logcat中
具体格式为
D/dalvikvm: <GC_Reason> <Amount_freed>, <Heap_stats>, <External_memory_stats>,<Pause_time>
GC_Reason
这个就表示的GC的理由
一般是4个理由,
第一个
GC_FOR_ALLOC
就是我们的堆已经满了,但是此时我们又创建了新的对象,这个对象已经放不进去堆了,就得先进行一个GC回收,然后才能放进去
这种 GC 是一种“阻塞”GC,意味着应用程序的所有线程会暂停(Stop-the-world),直到 GC 完成。这是强制性的垃圾回收,用于在没有足够内存分配新对象时,腾出空间。
影响:这种 GC 会对应用性能产生影响,因为在 GC 运行时,应用程序会被暂停。
第二个
GC_EXPLICIT
就是我们手动GC,比如调用
System.gc()
这是开发者或系统明确发起的 GC。在开发过程中,开发者可能希望强制进行垃圾回收以便释放内存,但是否真正执行完全取决于虚拟机。即便调用了 System.gc(),虚拟机也可能根据当前内存情况决定是否进行实际的回收。
影响:可能导致应用线程暂停,具体取决于内存情况和 GC 类型。通常不建议频繁调用 System.gc(),因为可能会增加不必要的内存回收开销
第三个
GC_HPROF_DUMP_HEAP
就是比如我们使用leakcanary来检测内存泄露,这个时候会创建一个HPROF文件,这个时候就会启动一个GC
为了确保内存快照的准确性,GC 会在生成 HPROF 文件之前进行一次完全的垃圾回收,以便从堆中回收所有不再使用的对象。
影响:GC_HPROF_DUMP_HEAP 会暂停应用程序,确保内存快照的准确性。在生成大文件时可能会造成较长时间的暂停。
第四个
GC_CONCURRENT
当堆内存即将填满时,虚拟机会尝试进行并发垃圾回收。这是为了在堆接近满时,主动回收内存,避免频繁触发 GC_FOR_ALLOC。
并发 GC 允许应用程序继续运行,同时在后台执行垃圾回收。应用线程在 GC 标记阶段的某些时刻可能会短暂地暂停,但整体上对应用程序的影响较小。
影响:相较于 GC_FOR_ALLOC,GC_CONCURRENT 影响较小,因为它是并发执行的,减少了应用程序的停顿时间。
Amount_freed
表示此时GC释放内存的大小
Heap_stats
表示堆的空闲内存百分比
External_memory_stats
现在高版本的已经不用了,懒得看了
Pause_time
我们进行GC的时候,会让线程暂停一下,这个Pause_time就表示暂停的时间


http://www.ppmy.cn/server/131768.html

相关文章

Web打点与WAF绕过技术具体实例(接上一篇)包含SQL注入绕过WAF、XSS绕过WAF、文件包含漏洞、命令注入

为了更好地理解和应用上一篇的Web打点与WAF绕过技术&#xff0c;本篇将通过具体示例进行详细说明。每个示例将在前面介绍的技术基础上&#xff0c;展示如何在实际场景中操作。 示例一&#xff1a;SQL注入绕过WAF 场景描述 我们在一个登录页面发现SQL注入漏洞&#xff0c;注入点…

应对网络安全挑战:App等保测评的重要性与策略

在全球数字化转型的大潮中&#xff0c;移动应用(App)作为连接人们日常生活与互联网世界的桥梁&#xff0c;其数量与日俱增&#xff0c;功能日趋多样化。与此同时&#xff0c;App背后潜藏的网络安全风险也随之上升&#xff0c;数据泄露、隐私侵犯、恶意软件植入等问题频发&#…

解锁编程的力量:SPL的学习之旅

SPL 一、前言二、集算器应用场景三、下载四、集算器的基本使用 一、前言 一种面向结构化数据的程序计算语言 集算器又称&#xff1a;SPL&#xff08;Structured Process Language&#xff09; 敏捷计算是集算器的主要特征 二、集算器应用场景 数据准备&#xff08;跑批&…

MTK免USB线实现WIFI Adb连接

文章目录 需求场景重置tcpip服务产品定义 实现方案修改文件修改内容 关联知识点总结 需求 MTK 平台在不用USB线的情况下&#xff0c;实现wifi adb 连接设备&#xff0c;从而进行调试操作。 场景 重置tcpip服务 MTK产品不像RK、全志系统产品&#xff0c;电脑和Android设备同一…

maven聚合ssm

如果没有写过ssm项目请移步SSM后端框架搭建&#xff08;有图有真相&#xff09;-CSDN博客 数据库准备 create table user (id int (11),uid varchar (60),name varchar (60),age int (11),sex varchar (12) ); insert into user (id, uid, name, age, sex) values(10,202409…

[LeetCode] 1047. 删除字符串中的所有相邻重复项

题目描述&#xff1a; 给出由小写字母组成的字符串 s&#xff0c;重复项删除操作会选择两个相邻且相同的字母&#xff0c;并删除它们。 在 s 上反复执行重复项删除操作&#xff0c;直到无法继续删除。 在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。 题目链接…

OpenCV视频I/O(20)视频写入类VideoWriter之用于将图像帧写入视频文件函数write()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::VideoWriter::write() 函数用于将图像帧写入视频文件。 该函数/方法将指定的图像写入视频文件。图像的大小必须与打开视频编写器时指定的大…

【云原生】利用 docker api 管理容器

目的 对容器进行管理 方案 使用 docker 官方sdk 对docker进行操作&#xff0c;操作依赖docker socket 官方sdk 参考 &#xff1a; https://docs.docker.com/engine/api/sdk/ 准备工作 为了方便调试&#xff0c;打开docker socket 的2375端口 部分代码 定义一个docker c…