Dalvik虚拟机垃圾收集(GC)过程分析
ART运行时垃圾收集(GC)过程分析

ART运行时垃圾收集(GC)过程分析ART运行时与Dalvik虚拟机一样,都使用了Mark-Sweep算法进行垃圾回收,因此它们的垃圾回收流程在总体上是一致的。
但是ART运行时对堆的划分更加细致,因而在此基础上实现了更多样的回收策略。
不同的策略有不同的回收力度,力度越大的回收策略,每次回收的内存就越多,并且它们都有各自的使用情景。
这样就可以使得每次执行GC时,可以最大限度地减少应用程序停顿。
本文就详细分析ART运行时的垃圾收集过程。
ART运行时的垃圾收集收集过程如图1所示:图1的最上面三个箭头描述触发GC的三种情况,左边的流程图描述非并行GC的执行过程,右边的流程图描述并行GC的执行流程,接下来我们就详细图中涉及到的所有细节。
在前面一文中,我们提到了两种可能会触发GC的情况。
第一种情况是没有足够内存分配请求的分存时,会调用Heap类的成员函数CollectGarbageInternal触发一个原因为kGcCauseForAlloc的GC。
第二种情况下分配出请求的内存之后,堆剩下的内存超过一定的阀值,就会调用Heap类的成员函数RequestConcurrentGC请求执行一个并行GC。
Heap类的成员函数RequestConcurrentGC的实现如下所示:[cpp] view plain copy 在CODE上查看代码片派生到我的代码片void Heap::RequestConcurrentGC(Thread* self) {// Make sure that we can do a concurrent GC.Runtime* runtime = Runtime::Current();DCHECK(concurrent_gc_);if (runtime == NULL || !runtime->IsFinishedStarting() ||!runtime->IsConcurrentGcEnabled()) {return;}{MutexLock mu(self, *Locks::runtime_shutdown_lock_);if (runtime->IsShuttingDown()) {return;}}if (self->IsHandlingStackOverflow()) {return;}// We already have a request pending, no reason to start more until we update// concurrent_start_bytes_.concurrent_start_bytes_ = std::numeric_limits<size_t>::max();JNIEnv* env = self->GetJniEnv();DCHECK(WellKnownClasses::java_lang_Daemons != NULL);DCHECK(WellKnownClasses::java_lang_Daemons_requestGC != NULL);env->CallStaticV oidMethod(WellKnownClasses::java_lang_Daemons,WellKnownClasses::java_lang_Daemons_requestGC); CHECK(!env->ExceptionCheck());}这个函数定义在文件art/runtime/gc/。
jvm的gc原理

jvm的gc原理JVM的GC原理一、概述JVM(Java虚拟机)是Java程序运行的环境,其中最重要的组成部分之一就是垃圾回收(Garbage Collection,简称GC)机制。
GC的作用是自动管理程序中的内存,及时释放不再使用的对象,以避免内存泄漏和内存溢出的问题。
本文将对JVM的GC原理进行详细介绍。
二、垃圾回收算法1. 标记-清除算法标记-清除算法是最基本的垃圾回收算法之一。
它的过程分为两个阶段:标记阶段和清除阶段。
在标记阶段,GC会从根节点(一般是程序中的静态变量和栈中的引用)开始,递归地遍历对象图,标记出所有被引用的对象。
在清除阶段,GC会遍历整个堆,清除所有未被标记的对象。
2. 复制算法复制算法是针对标记-清除算法的改进。
它将堆分为两个区域,每次只使用其中一个区域。
当一个区域的对象被标记后,将其复制到另一个区域中,然后清除原来的区域。
这样可以解决碎片问题,但是需要额外的空间来存储复制的对象。
3. 标记-整理算法标记-整理算法是对标记-清除算法的改进。
它的过程与标记-清除算法类似,但是在清除阶段,标记-整理算法会将存活的对象向一端移动,然后清除边界外的所有对象。
这样可以解决碎片问题,并且不需要额外的空间。
4. 分代算法分代算法是针对对象的生命周期不同而提出的。
一般来说,对象的生命周期可以分为年轻代和老年代。
年轻代中的对象生命周期较短,老年代中的对象生命周期较长。
分代算法将堆分为年轻代和老年代两个区域,分别采用不同的垃圾回收算法。
年轻代一般使用复制算法,老年代一般使用标记-清除算法或标记-整理算法。
三、GC的执行过程1. 初始标记初始标记阶段是GC的第一步,它的目的是标记出所有的根对象,并且停止所有的应用线程。
这个过程是短暂的,因为只需要标记出与根对象直接关联的对象。
2. 并发标记并发标记阶段是GC的核心步骤,它的目的是通过并发执行来标记出所有的存活对象。
在这个阶段,GC会遍历整个堆,标记出与根对象直接或间接关联的存活对象。
垃圾回收(GC)的三种基本方式

垃圾回收(GC)的三种基本⽅式垃圾回收(GC)的三种基本⽅式 垃圾:就是程序需要回收的对象,如果⼀个对象不在被直接或者间接地引⽤,那么这个对象就成为了垃圾,它占⽤的内存需要及时地释放,否则就会引起内存泄漏。
这⾥可以⼤致的分为两类:跟踪回收,引⽤计数。
垃圾回收统⼀理论⼀⽂阐述了⼀个理论:任何垃圾回收的思路,⽆⾮以上两种的组合,其中⼀种的改善和进步,必然伴随着另⼀种的改善和进步。
跟踪回收: 跟踪回收的⽅式独⽴于程序,定期运⾏来检查垃圾,需要较长时间的中断。
标记清除: 标记清除的⽅式需要对程序的对象进⾏两次扫描,第⼀次从根(root)开始扫描,被根引⽤了的对象标记为不是垃圾,不是垃圾的对象引⽤的对象同样标记为不是垃圾,以此递归。
所有不是垃圾的对象的引⽤都扫描完了之后。
就进⾏第⼆次扫描,第⼀次扫描中没有得到标记的对象就是垃圾了,对此进⾏回收、复制收集 复制收集的⽅式只需要对对象进⾏⼀次扫描。
准备⼀个新的空间,从根开始,对对象进⾏扫描,如果存在对这个对象的引⽤,就把它复制到新空间中,⼀次扫描结束之后,所有存在于新空间的对象就是所有的⾮垃圾对象。
这两种⽅式各有千秋,标记清除的⽅式节省内存但是两次扫描需要更多的时间,对于垃圾⽐较⼩的情况占由优势。
复制收集更快速但是需要额外开辟⼀块⽤来复制的内存,对垃圾⽐例较⼤的情况占优势。
特别的,复制收集由局部性的优点。
在复制收集的过程中,会按照对象被引⽤的顺序将对戏那个复制到新空间中,于是,关系⽐较近的对象被放在距离较近的内存空间的可能性会提⾼,这叫做局部性。
局部性⾼的情况下,内存缓存会更有效地运作,程序的性能会提⾼。
对于标记清除,有⼀种标记-压缩算法的衍⽣算法:对于压缩阶段,它的⼯作就是移动所有的可达对象到堆内存的同⼀个区域中,使他们紧凑的排列在⼀起,从⽽将所有⾮可达对象释放出来的空闲内存都集中在⼀起,通过这样的⽅式来⼤⼑减少内存碎⽚的⽬的。
引⽤计数引⽤计数是指,针对每个对象,保存⼀个对该对象的引⽤计数,该对象的引⽤增加,则相应的引⽤计数增加,如果该对象的引⽤计数为0,则回收该对象。
jvm内存垃圾回收机制

Java虚拟机(JVM)的内存垃圾回收机制主要涉及自动内存管理和垃圾回收两个核心功能。
自动内存管理主要是针对对象内存的回收和对象内存的分配。
JVM的堆是垃圾收集器管理的主要区域,也被称作GC堆(Garbage Collected Heap)。
大部分情况下,对象都会首先在Eden区域分配。
在一次新生代垃圾回收后,如果对象还存活,则会进入s0或者s1,并且对象的年龄还会加1(Eden区->Survivor区后对象的初始年龄变为1),当它的年龄增加到一定程度(默认为15岁),就会被晋升到老年代中。
垃圾回收是JVM自动内存管理的另一个重要方面。
主要有两种通用的垃圾回收方法:引用计数法和可达性分析算法。
引用计数法为每个对象增加一个计数器,当该对象被引用时,计数器加一,当引用失效时,计数器减一。
当计数器为零时,该对象就可以被回收了。
这种方法无法解决循环引用的问题。
可达性分析算法是通过GC Root的对象作为起始节点,通过引用向下搜索,所走过的路径称为引用链。
当对象没有任何一条引用链链接的时候,就会被认定为垃圾。
可作为GC Root的对象包括:类加载器,Thread,虚拟机栈的本地变量表,static成员,常量引用,本地方法栈的变量等等。
大部分情况下,对象都会首先在Eden区域分配,在一次新生代
垃圾回收后,如果对象还存活,则会进入s0或者s1,并且对象的年龄还会加1(Eden区->Survivor区后对象的初始年龄变为1),当它的年龄增加到一定程度(默认为15岁),就会被晋升到老年代中。
以上信息仅供参考,如果还想了解更多信息或遇到相关问题,建议咨询专业人士。
JVM系列(二):JVM的4种垃圾回收算法、垃圾回收机制与总结

垃圾回收算法1.标记清除标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。
在标记阶段首先通过根节点(GC Roots),标记所有从根节点开始的对象,未被标记的对象就是未被引用的垃圾对象。
然后,在清除阶段,清除所有未被标记的对象。
适用场合:●存活对象较多的情况下比较高效●适用于年老代(即旧生代)缺点:●容易产生内存碎片,再来一个比较大的对象时(典型情况:该对象的大小大于空闲表中的每一块儿大小但是小于其中两块儿的和),会提前触发垃圾回收●扫描了整个空间两次(第一次:标记存活对象;第二次:清除没有标记的对象)2.复制算法从根集合节点进行扫描,标记出所有的存活对象,并将这些存活的对象复制到一块儿新的内存(图中下边的那一块儿内存)上去,之后将原来的那一块儿内存(图中上边的那一块儿内存)全部回收掉现在的商业虚拟机都采用这种收集算法来回收新生代。
适用场合:●存活对象较少的情况下比较高效●扫描了整个空间一次(标记存活对象并复制移动)●适用于年轻代(即新生代):基本上98%的对象是”朝生夕死”的,存活下来的会很少缺点:●需要一块儿空的内存空间●需要复制移动对象3.标记整理复制算法的高效性是建立在存活对象少、垃圾对象多的前提下的。
这种情况在新生代经常发生,但是在老年代更常见的情况是大部分对象都是存活对象。
如果依然使用复制算法,由于存活的对象较多,复制的成本也将很高。
标记-压缩算法是一种老年代的回收算法,它在标记-清除算法的基础上做了一些优化。
首先也需要从根节点开始对所有可达对象做一次标记,但之后,它并不简单地清理未标记的对象,而是将所有的存活对象压缩到内存的一端。
之后,清理边界外所有的空间。
这种方法既避免了碎片的产生,又不需要两块相同的内存空间,因此,其性价比比较高。
4.分代收集算法分代收集算法就是目前虚拟机使用的回收算法,它解决了标记整理不适用于老年代的问题,将内存分为各个年代。
一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),在堆区之外还有一个代就是永久代(Permanet Generation)。
JVM虚拟机(四):JVM垃圾回收机制概念及其算法

JVM虚拟机(四):JVM垃圾回收机制概念及其算法垃圾回收概念和其算法谈到垃圾回收(Garbage Collection)GC,需要先澄清什么是垃圾,类⽐⽇常⽣活中的垃圾,我们会把他们丢⼊垃圾箱,然后倒掉。
GC中的垃圾,特指存于内存中、不会再被使⽤的对象,⼉回收就是相当于把垃圾“倒掉”。
垃圾回收有很多中算法:如引⽤计数法、标记压缩法、复制算法、分代、分区的思想。
垃圾收集算法引⽤计数法:就是个⽐较古⽼⽽经典的垃圾收集算法,其核⼼就是在对象被其他所引⽤计数器加1,⽽当引⽤时效时则减1,但是这种⽅式有⾮常严重的问题:⽆法处理循环引⽤的情况、还有就是每次进⾏加减操作⽐较浪费系统性能。
标记清除法:分为标记和清除两个阶段进⾏处理内存中的对象,当然这种⽅式也有⾮常⼤的弊端,就是空间碎⽚问题,垃圾回收后的空间不是连续的,不连续的内存空间的⼯作效率要低于连续的内存空间。
复制算法:其核⼼思想就是将内存空间分为两块,每次只使⽤其中⼀块,在垃圾回收时,将正在使⽤的内存中的存留对象复制到未被使⽤的内存块中去,之后去清除之前正在使⽤的内存快中的所有的对象,反复去交换两个内存的⾓⾊,完成垃圾收集。
(java中的新⽣代的from和to空间使⽤的就是这个算法)标记压缩法:标记压缩法在标记清除基础之上做了优化,把存活的对象压缩到内存⼀端,⽽后进⾏垃圾清理。
(java中⽼年代使⽤的就是标记压缩法)图解新⽣代使⽤的复制算法:⽂字说明:新⽣代中没有GC过的对象在eden区,然后进⾏GC⼀次,进⼊到s0区。
然后再次进⾏GC得时候,就回去S0区去查看这个对象有没有在使⽤,如果在使⽤那就把这个对象复制到s1区,然后清除s0区不再使⽤的对象。
再次GC的时候就去S1区,再看看这个对象有没有在使⽤,如果还在使⽤,那就复制到S0区。
然后清除S1区不在使⽤的对象。
图解⽼年代算法:⽂字说明:进⾏GC时看看⽼年代有没有在使⽤的对象,如果有那么就压缩出⼀个区域把那些实⽤的对象放到压缩的区域中,然后把不再使⽤的对象全部回收掉。
GC垃圾回收(四个算法)

GC垃圾回收(四个算法)垃圾回收(GC)是现代编程语言中的一项重要功能,它的目的是回收不再使用的内存。
随着程序复杂性的增加以及内存分配的动态性,垃圾回收成为了必不可少的组成部分。
1.引用计数:引用计数是最简单的垃圾回收算法之一、它通过记录每个对象的引用计数来确定是否回收该对象。
当一个对象被引用时,其引用计数加一;当一个对象的引用被释放时,其引用计数减一、当引用计数为零时,该对象即为垃圾。
引用计数的优点在于实时性好,对象一旦变为垃圾就会被立即回收,不会造成内存的过度占用。
然而,引用计数的缺点也很明显,即无法解决循环引用的问题。
如果存在循环引用,对象之间将永远无法达到引用计数为零的状态,导致内存泄漏。
2.标记清除:标记清除算法通过两个阶段来进行垃圾回收。
首先,从根对象出发,标记所有可达对象。
然后,在第二阶段,系统将遍历所有对象,并清除未标记的对象。
标记清除算法相较于引用计数算法,能够解决循环引用的问题。
它利用可达性分析来确定对象是否仍然被引用,从而决定是否回收。
然而,标记清除算法的不足是在执行清除操作时,会产生内存碎片。
这些碎片可能会导致大量的内存分配时间,从而影响程序的性能。
3.复制收集:复制收集算法是一种高效的垃圾回收算法。
它将内存分为两个部分:From空间和To空间。
在垃圾回收过程中,所有存活的对象将被复制到To空间中,而垃圾对象则将被回收。
复制收集算法能够高效地回收垃圾对象,并解决内存碎片问题。
然而,复制收集算法的缺点是它需要额外的内存空间来完成复制操作,并且在复制过程中,需要更新所有指向存活对象的引用。
4.标记整理:标记整理算法是标记清除算法的改进版。
它首先进行标记操作,然后在清除操作之前,将所有存活的对象向一端移动,以解决内存碎片问题。
标记整理算法在性能方面优于标记清除算法,因为它能够完全避免内存碎片。
然而,与标记清除算法一样,标记整理算法也需要执行两个阶段的操作,其中标记阶段可能会占用较长的时间。
dart gc回收流程

dart gc回收流程
Dart的GC回收流程可以分为年轻代GC和老年代GC。
年轻代GC主要针对寿命较短的Object,在对象分配内存时会分配一个连续的逻辑内存,它分成大小相同的两部分,其中一半置为活动状态,另一部分置为非活动状态。
新生产对象被连续的分配到活动空间。
当活动空间被填满,GC收集器将从GC Roots为起点搜索,搜索通过的路径称之为引用链,当一个对象存在基于根对象的引用链时,称之为引用对象,当没有与根对象建立引用链,称之为未引用对象,即使两个对象循环引用,但没有与根对象的路径,也称之为未引用对象。
如图五所示,收集器检查所有的引用对象同时将引用对象移动到另一半空间(非活动状态空间)。
移动完成之后,将非活动空间变更为活动空间,活动空间清空并标记为非活动状态。
当对象经历一定次数的年轻代GC仍然存活,或者其生命周期较长,将其放入老年代区域。
老年代GC采用mark-sweep方法回收对象,分为两个阶段:第一阶段,首先遍历对象图,然后标记仍在使用的对象;第二阶段,将扫描整个内存,并且回收所有未标记的对象。
Dart的GC回收机制可以有效地管理内存,优化应用的性能和响应速度。
在开发过程中,了解和运用Dart的GC回收流程可以更好地编写高效的代码。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Dalvik虚拟机垃圾收集(GC)过程分析前面我们分析了Dalvik虚拟机堆的创建过程,以及Java对象在堆上的分配过程。
这些知识都是理解Dalvik虚拟机垃圾收集过程的基础。
垃圾收集是一个复杂的过程,它要将那些不再被引用的对象进行回收。
一方面要求Dalvik虚拟机能够标记出哪些对象是不再被引用的。
另一方面要求Dalvik虚拟机尽快地回收内存,避免应用程序长时间停顿。
本文就将详细分析Dalvik虚拟机是如何解决上述问题完成垃圾收集过程的。
Dalvik虚拟机使用Mark-Sweep算法来进行垃圾收集。
顾名思义,Mark-Sweep算法就是为Mark和Sweep两个阶段进行垃圾回收。
其中,Mark阶段从根集(Root Set)开始,递归地标记出当前所有被引用的对象,而Sweep阶段负责回收那些没有被引用的对象。
在分析Dalvik虚拟机使用的Mark-Sweep算法之前,我们先来了解一下什么情况下会触发GC。
Dalvik虚拟机在三种情况下会触发四种类型的GC。
每一种类型GC使用一个GcSpec结构体来描述,它的定义如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
struct GcSpec {
/* If true, only the application heap is threatened. */
bool isPartial;
/* If true, the trace is run concurrently with the mutator. */
bool isConcurrent;
/* Toggles for the soft reference clearing policy. */
bool doPreserve;
/* A name for this garbage collection mode. */
const char *reason;
};
这个结构体定义在文件dalvik/vm/alloc/Heap.h中。
GcSpec结构体的各个成员变量的含义如下所示:
isPartial: 为true时,表示仅仅回收Active堆的垃圾;为false时,表示同时回收Active堆和Zygote堆的垃圾。
isConcurrent: 为true时,表示执行并行GC;为false时,表示执行非并行GC。
doPreserve: 为true时,表示在执行GC的过程中,不回收软引用引用的对象;为false时,表示在执行GC的过程中,回收软引用引用的对象。
reason: 一个描述性的字符串。
Davlik虚拟机定义了四种类的GC,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
/* Not enough space for an "ordinary" Object to be allocated. */
extern const GcSpec *GC_FOR_MALLOC;
/* Automatic GC triggered by exceeding a heap occupancy threshold. */
extern const GcSpec *GC_CONCURRENT;
/* Explicit GC via Runtime.gc(), VMRuntime.gc(), or SIGUSR1. */
extern const GcSpec *GC_EXPLICIT;
/* Final attempt to reclaim memory before throwing an OOM. */
extern const GcSpec *GC_BEFORE_OOM;
这四个全局变量声明在文件dalvik/vm/alloc/Heap.h中。
它们的含义如下所示:
GC_FOR_MALLOC: 表示是在堆上分配对象时内存不足触发的GC。
GC_CONCURRENT: 表示是在已分配内存达到一定量之后触发的GC。
GC_EXPLICIT: 表示是应用程序调用System.gc、VMRuntime.gc接口或者收到SIGUSR1信号时触发的GC。
GC_BEFORE_OOM: 表示是在准备抛OOM异常之前进行的最后努力而触发的GC。
实际上,GC_FOR_MALLOC、GC_CONCURRENT和GC_BEFORE_OOM三种类型的GC都是在分配对象的过程触发的。
在前面一文,我们提到,Dalvik虚拟机在Java堆上分配对象的时候,在碰到分配失败的情况,会尝试调用函数gcForMalloc进行垃圾回收。
函数gcForMalloc的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
static void gcForMalloc(bool clearSoftReferences)
{
......
const GcSpec *spec = clearSoftReferences ? GC_BEFORE_OOM : GC_FOR_MALLOC;
dvmCollectGarbageInternal(spec);
}
这个函数定义在文件dalvik/vm/alloc/Heap.cpp中。
参数clearSOftRefereces表示是否要对软引用引用的对象进行回收。
如果要对软引用引用的对象进行回收,那么就表明当前内存是非常紧张的了,因此,这时候执行的就是GC_BEFORE_OOM类型的GC。
否则的话,执行的就是GC_FOR_MALLOC类型的GC。
它们都是通过调用函数dvmCollectGarbageInternal来执行的。
在前面一文,我们也提到,当Dalvik虚拟机成功地在堆上分配一个对象之后,会检查一下当前分配的内存是否超出一个阀值,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void* dvmHeapSourceAlloc(size_t n)
{
......
HeapSource *hs = gHs;
Heap* heap = hs2heap(hs);
if (heap->bytesAllocated + n > hs->softLimit) {
......
return NULL;
}
void* ptr;
if (gDvm.lowMemoryMode) {
......
ptr = mspace_malloc(heap->msp, n);
......
} else {
ptr = mspace_calloc(heap->msp, 1, n);
......
}
countAllocation(heap, ptr);
......
if (heap->bytesAllocated > heap->concurrentStartBytes) {
......
dvmSignalCond(&gHs->gcThreadCond);
}
return ptr;
}
这个函数定义在文件dalvik/vm/alloc/HeapSource.cpp中。
函数dvmHeapSourceAlloc成功地在Active堆上分配到一个对象之后,就会检查Active堆当前已经分配的内存(heap->bytesAllocated)是否大于预设的阀值(heap->concurrentStartBytes)。
如果大于,那么就会通过条件变量gHs->gcThreadCond唤醒GC线程进行垃圾回收。
预设的阀值(heap->concurrentStartBytes)是一个比指定的堆最小空闲内存小128K的数值。
也就是说,当堆的空闲内不足时,就会触发GC_CONCURRENT类型的GC。
GC线程是Dalvik虚拟机启动的过程中创建的,它的执行体函数是gcDaemonThread,实现如下所示:。