`

Dalvik虚拟机的垃圾回收

 
阅读更多

http://blog.csdn.net/caimouse/article/details/6940419

 

垃圾回收技术已经出现很久了,可以追溯到 20 世纪 60 年代,在 LISP 语言中就开始进行应用,而后的 Smalltalk,java,c# 等 语言更是一步一步地将其推向新的高潮。它广受技术专家的推崇,并被高度的评价,被认为是提高软件质量和生产力的一个有效的银弹,是一个具有革命性技术。由 于计算机的内存资源总是有限的,为了不同的程序运行,必须把不需要使用的内存回收,以便重新使用。假如那一天计算机的内存足够大,可以一年内创建的对象, 所占用的内存都卓卓有余时,就没有必要使用这种回收技术了。在C++/C的世界里,没有回收技术,其实就是需要开发人员自己负责把它使用完成的内存,主动 去删除它。在D alvik 虚拟机实现里,虽然它跟 java 虚拟机有本质上的区别,但在内存回收这一块,是没有区别的,可能使用垃圾回收的技术,就是一样的。因此,需要先学习计算领域里典型的垃圾回收算法。

 

在垃圾回收技术里,经典的算法主要有以下三种:引用计数、 MarkSweep 算法、 SemiSpaceCopy 算法。其它算法或者混合以上三种法来使用,根据不同的场合来选择不同的算法。

一、引用计数

    这 种技术非常简单,就是使用一个变量记录这块内存或者对象的使用次数。比如在COM技术里,就是使用引用计数来确认这个COM对象什么时候删除的。当一个 COM对象给不同线程来使用时,由于不同的线程生命周期不一样,因此,没有办法知道这个COM对象到底在那个线程删除,只能使用引用计数来删除,否则还需 要不同线程之间添加同步机制,这样是非常麻烦和复杂的,如果COM对象有很多,就变成基本上不能实现了。引用计数的优点是:在对象变成垃圾时,可以马上进 行回收,回收效率和成本都是最低。因此,内存使用率最高,基本上没有时间花费,不需要把所有访问COM对象线程都停下来。缺点是:引用计数会影响执行效 率,每引用一次都需要更新引用计数,对于COM对象那是人工控制的,因此次数很少,没有什么影响。但在 Java 里是由编译程序来控制的,因此引用次数非常多。另外一个问题就是引用计数不能解决交叉引用,或者环形引用的问题。比如在一个环形链表里,每一个元素都引用前面的元素,这样首尾相连的链表,当所有元素都变成不需要时,就没有办法识别出来,并进行内存回收。

二、 Mark Sweep 算法

标 记-清除算法依赖于对所有存活对象进行一次全局遍历来确定哪此对象可以回收,遍历的过程从根出发,找到所有可到达对象,其它不可到达的对象就是垃圾对象, 可被回收。正如其名称所暗示的那样,这个算法分为两大阶段:标记和清除。这种分步执行的思路构成了现代垃圾收集算法的思想基础。与引用计数算法不同的是, 标记-清除算法不需要监测每一次内存分配和指针操作,只需要在标记阶段进行一次统计就行了。标记-清除算法可以非常自然的处理环形问题,另外在创建对象和 销毁对象时少了操作引用计数值的开销。不过,标记-清除算法也有一个缺点,就是需要标记和清除阶段中把所有对象停止执行。在垃圾回收器运行过程中,应用程 序必须暂时停止,并等到垃圾回收器全部运行完成后,才能重新启动应用程序运行。

 

下面就来先看看 Dalvik 虚拟机整个标记和清除中使用到那些函数,在文件 alloc/MarkSweep.h 里有函数 如下:

1 )调用函数 dvmHeapBeginMarkStep 来创建位图,并从对象位图里拷贝一份位图出来,以便后面对这个位图进行标记。

2 )调用函数 dvmHeapMarkRootSet 对所有根对象进行标记。

3 )调用函数 dvmHeapScanMarkedObjects 根据上一个函数给出的根对象位图,对每一个根相关的位图进行计算,如果这个根对象有被引用,就标记为使用。这个过程是递归调用的过程,从根开始不断重复地对子树进行标记的过程。

4 )调用函数 dvmHeapHandleReferences JAVA 类对象的引用类型进行处理。主要处理三个直接的了类 :SoftReference,WeakReference,PhantomReference SoftReference 对象封装了对引用目标的“软引用”; WeakReference 封装了对引用目标的“弱引用”;而 PhantomReference 封装了对引用目标的“影子引用。强引用禁止引用目标被垃圾收集,而软引用、弱引用和影子引用不禁止。

5 )调用函数 dvmHeapScheduleFinalizations 对未曾标记的对象进行完成调用,让每一个对象最后删除动作可以运行,以便后面从内存里把对象删除,相当于对象的析构作用。

6 )调用函数 dvmHeapSweepUnmarkedObjects 对未曾标记的对象进行清除操作,也就是删除没有再使用的对象。

7 )调用函数 dvmHeapFinishMarkStep 对已经删除的对象进行内存回收,可以调用堆管理函数改变目前堆使用的内存,并整理内存,就可以得到更多空闲的内存了。

 

这个过程,就是 Dalvik 虚拟机的整个标记和删除的算法过程,实际的代码会相当复杂,算法上是很清楚的,就是细节、时间方面要求相当严格,否则乱删除还在使用的对象,就导致整个虚拟机运行出错。

 

通过上面的学习,了解了垃圾回收的原理和过程。那么 Dalvik 虚 拟机是什么时候进行垃圾回收呢?要回答这个问题,那么得继续分析代码,继续进入下面的学习。其实,垃圾回收主要有两种方式,一种是虚拟机线程自动进行的, 一种是手动进行的。现在先来学习自动进行的方式,所谓自动方式,就是虚拟机创建一个线程,这个线程定时进行。虚拟机在初始化时,就进行创建这个线程,如下 的代码:

if (gDvm. zygote ){

if (!dvmInitZygote())

goto fail;

} else {

if (!dvmInitAfterZygote())

goto fail;

}

 

在上面这段代码里调用函数 dvmInitAfterZygote ,在这个函数里就会调用函数 dvmSignalCatcherStartup 来创建垃圾回收线程,这个函数的代码如下:

bool dvmSignalCatcherStartup ( void )

{

gDvm. haltSignalCatcher = false ;

 

if (!dvmCreateInternalThread(&gDvm. signalCatcherHandle ,

"SignalCatcher" , signalCatcherThreadStart,NULL))

return false ;

 

return true ;

}

 

通过上面的这段代码,就可以看到线程运行函数是 signalCatcherThreadStart ,在这个函数里就会调用函数 dvmCollectGarbage 来进行垃圾回收。代码如下:

void dvmCollectGarbage (bool collectSoftReferences)

{

dvmLockHeap();

 

LOGVV( "ExplicitGC\n" );

dvmCollectGarbageInternal(collectSoftReferences);

 

dvmUnlockHeap();

}

 

在这个函数主要通过锁来锁住多线程访问的堆空间相关对象,然后直接就调用函数 dvmCollectGarbageInternal 来进行垃圾回收过程了,也就调用上面标记删除算法的函数。

 

另一种方式通过调用运行库的 GC 来回收,如下:

/*

* public void gc ()

*

* Initiate a gc .

*/

static void Dalvik_java_lang_Runtime_gc ( const u4 * args, JValue *pResult)

{

UNUSED_PARAMETER(args);

 

dvmCollectGarbage( false );

RETURN_VOID();

}

 

在这里也是调用函数 dvmCollectGarbage 来进行垃圾回收。手动的方式适合当需要内存,但线程又没有调用时进行。

 

现在开始学习虚拟机的初始化过程,先从 dvmStartup 函数开始,这个函数实现所有开始虚拟机的准备工作。

dvmAllocTrackerStartup 函数初始化跟踪显示系统,跟踪系统主要用生成调试系统的数据包。

dvmGcStartup 函数是用来初始化垃圾回收器。

dvmThreadStartup 函数是初始化线程列表和主线程环境参数。

dvmInlineNativeStartup 函数是分配内部操作方法的表格内存。

dvmVerificationStartup 函数是初始化虚拟机的指令码相关的内容,以便检查指令是否正确。

dvmRegisterMapStartup 函数是分配指令寄存器状态的内存。

dvmInstanceofStartup 函数是分配虚拟机使用的缓存。

dvmClassStartup 函数是初始化虚拟机最基本用的 JAVA 库。

dvmThreadObjStartup 函数是初始化虚拟机进一步使用的 JAVA 类库线程类。

dvmExceptionStartup 函数是初始化虚拟机使用的异常 JAVA 类库。

dvmStringInternStartup 函数是初始化虚拟机解释器使用的字符串哈希表。

dvmNativeStartup 函数是初始化本地方法库的表。

dvmInternalNativeStartup 函数是初始化内部本地方法,建立哈希表,方便快速查找到。

dvmJniStartup 函数是初始化 JNI 调用表,以便快速找到本地方法调用的入口。

dvmReflectStartup 函数是缓存 JAVA 类库里的反射类。

 

接着把下面这些类先进行初始化,如下:

static const char *earlyClasses[] = {

" Ljava / lang /InternalError;" ,

" Ljava / lang /StackOverflowError;" ,

" Ljava / lang /UnsatisfiedLinkError;" ,

" Ljava / lang /NoClassDefFoundError;" ,

NULL

};

初始化这些类,就是调用函数 dvmFindSystemClassNoInit 来初始化

 

接着调用 dvmValidateBoxClasses 函数来初始化 JAVA 基本类型库,如下:

static const char *classes[] = {

" Ljava / lang /Boolean;" ,

" Ljava / lang /Character;" ,

" Ljava / lang /Float;" ,

" Ljava / lang /Double;" ,

" Ljava / lang /Byte;" ,

" Ljava / lang /Short;" ,

" Ljava / lang /Integer;" ,

" Ljava / lang /Long;" ,

NULL

};

这些类调用函数,不是上面使用系统函数来初始化,而是调用 dvmFindClassNoInit 来初始化。

 

调用 dvmPrepMainForJni 函数准备主线程里的解释栈可以调用 JNI 的方法;调用 registerSystemNatives 来注册 JAVA 库里的 JNI 方法;调用 dvmCreateStockExceptions 函数分配异常出错的内存;调用 dvmPrepMainThread 函数完成解释器主线程的初始化;调用 dvmDebuggerStartup 函数进行调试器的初始化;

最后调用 dvmInitZygote 或者 dvmInitAfterZygote 来初始化线程的模式,调用 dvmCheckException 函数检查是否有异常情况出现。

 

到这里就把整个虚拟机初始化流程完成。

 

Dalvik 虚拟机里,提供了一些 JNI 的调用测试函数,以便确认 JNI 的机制是否可以运 行, JNI 调用效率是否达到设计的目标,它是通过在 registerSystemNatives 函数初始化,然后调用 jniRegisterSystemMethods 函数来设置 JNI 函数。

 

JNI 的测试函数代码如下:

/*

* JNI registration

*/

static JNINativeMethod gMethods[] = {

/*name, signature, funcPtr */

{ "emptyJniStaticMethod0" , "()V" , emptyJniStaticMethod0 },

{ "emptyJniStaticMethod6" , "(IIIIII)V" ,emptyJniStaticMethod6 },

{ "emptyJniStaticMethod6L" ,

"( Ljava / lang /String;[ Ljava / lang /String;[[I"

" Ljava / lang /Object;[ Ljava / lang /Object;[[[[ Ljava / lang /Object;)V" ,

emptyJniStaticMethod6L },

};

这段代码是提供 JNI 测试函数的接口和相关实现的 C 函数入口。

 

int register_org_apache_harmony_dalvik_NativeTestTarget ( JNIEnv *env)

{

int result = jniRegisterNativeMethods(env,

" org / apache /harmony/ dalvik /NativeTestTarget" ,

gMethods,NELEM(gMethods));

这段代码把 JNI 调用接口设置到包 org.apache.harmony.dalvik.NativeTestTarget 下面,这样在 Java 的应用程序里就可以调用了。

 

if (result != 0) {

/*print warning, but allow to continue */

LOGW( "WARNING:NativeTestTarget not registered\n" );

(*env)-> ExceptionClear (env);

}

return 0;

}

 

/*

* public static voidemptyJniStaticMethod0()

*

* For benchmarks, a do-nothingJNI method with no arguments.

*/

static void emptyJniStaticMethod0 ( JNIEnv *env, jclass clazz)

{

//This space intentionally left blank.

}

 

可见这些系统函数的代码,都空的结构,没有真实的代码运行,就可以用来测试 JNI 是否可以工作,测试 JNI 调用的时间需要多少,可以提供准确的时间。

分享到:
评论

相关推荐

    Android Dalvik虚拟机结构及机制剖析 第2卷.pdf

    但我可以根据标题所揭示的主题,即“Android Dalvik虚拟机结构及机制剖析”,来生成相关的知识点。 Dalvik是Android操作系统中用于执行应用程序的虚拟机。它专为移动设备优化,使得Android应用能够高效运行。Dalvik...

    dalvik虚拟机运行过程分析

    7. **垃圾回收**:Dalvik虚拟机采用非分代的垃圾回收机制,即单一的垃圾收集器。当内存不足时,垃圾回收器会扫描堆内存,回收不再引用的对象所占用的空间。 8. **线程管理**:除了主线程外,Dalvik虚拟机还负责创建...

    Dalvik虚拟机垃圾收集过程分析.doc

    《Dalvik虚拟机垃圾收集过程详解》 Dalvik虚拟机,作为Android系统早期的核心组件,其内存管理机制中的垃圾收集(Garbage Collection, GC)扮演着至关重要的角色。垃圾收集的主要任务是识别并释放不再被程序引用的...

    Dalvik虚拟机垃圾收集(GC)过程分析.docx

    ### Dalvik虚拟机垃圾收集(GC)过程分析 #### 垃圾收集的基本概念与重要性 垃圾收集(Garbage Collection, GC)是一项自动化管理内存的技术,它自动识别并回收那些不再使用的对象所占用的内存空间,从而避免了内存...

    Android Dalvik虚拟机结构及机制剖析 第1卷.pdf

    5. 垃圾回收机制:Dalvik虚拟机内部实现了垃圾回收(GC)机制,它负责自动管理内存,释放不再使用的对象占用的内存空间。GC机制对于防止内存泄漏、提高应用性能至关重要。 6. 进程与线程管理:在Android中,每个...

    Android Dalvik虚拟机结构及机制剖析第2卷

    3. **垃圾回收机制**:由于Android设备内存有限,Dalvik虚拟机采用了特殊的垃圾回收策略,如可达性分析,自动回收不再使用的对象,以释放内存资源。 4. **线程管理**:Dalvik虚拟机支持多线程,每个Android应用程序...

    Android虚拟机Dalvik(略谈Android Dalvik虚拟机)

    它采用了一系列节能措施,如优化的垃圾回收算法等,以减少对电池的影响。 #### 四、Dalvik虚拟机的历史背景与行业影响 ##### 1. 行业背景 随着Google发布Android SDK,Dalvik虚拟机作为Android平台的核心组成部分...

    《Android Dalvik虚拟机结构及机制剖析 Dalvik虚拟机各模块机制分析 第2卷》PDF

    2. **优化的垃圾回收**:Dalvik虚拟机的垃圾回收机制对于移动设备来说至关重要,因为它能够有效地管理有限的内存资源。书中可能详细介绍了如何实现并发标记清除算法,并讨论了如何减少暂停时间以提升用户体验。 3. ...

    Android Dalvik虚拟机讲义

    - **垃圾回收**: Dalvik虚拟机使用了自己的垃圾回收机制,有效地管理内存,防止内存泄漏。 2. **Dalvik Just-In-Time (JIT) 编译** - 在早期版本的Android中,Dalvik采用解释执行方式,后来引入了JIT编译技术,将...

    Dalvik虚拟机内存管理

    ### Dalvik虚拟机内存管理详解 #### 一、引言 Dalvik虚拟机作为Google针对Android平台设计的专有Java虚拟机实现,在Android系统中扮演着核心角色。它负责执行应用层代码,并提供了高效的内存管理和垃圾回收机制。...

    Android Dalvik虚拟机结构及机制剖析第1卷

    Dalvik虚拟机采用非分代垃圾回收策略,主要关注对象的可达性。当对象不再被引用时,垃圾回收器会自动释放内存,避免内存泄漏。理解这一机制对于优化应用性能至关重要。 4. **Just-In-Time (JIT) 编译**: 从...

    Dalvik虚拟机源码

    垃圾回收** Dalvik虚拟机的垃圾收集器是Stop-The-World类型的,即在GC过程中,所有线程都会暂停。虽然会影响性能,但鉴于移动设备资源有限,这种设计有助于减少内存碎片。 **6. 安全机制** Dalvik虚拟机实施了...

    dalvik虚拟机介绍

    - **高效的内存管理**:Dalvik虚拟机实现了对象生命周期、堆栈、线程、安全、异常管理和垃圾回收,适应移动设备资源限制。 - **DEX文件的优化**:包括字段字节序调整、对齐优化、类验证和方法内操作码优化,提升...

    Dalvik虚拟机架构

    3. **垃圾回收机制**:考虑到移动设备对内存使用的要求,Dalvik虚拟机实现了高效且低延迟的垃圾回收机制。这种机制能够更好地适应移动设备的特点,减少了对用户体验的影响。 4. **类加载器**:Dalvik虚拟机中的类...

    虚拟机 dalvik4.2 源代码

    4. **垃圾回收**:Dalvik虚拟机使用了一种称为"分代垃圾回收"的策略,针对不同生命周期的对象进行不同的处理。源代码中包含了多种垃圾回收算法,如Mark-Sweep和Copy算法。 5. **优化与调试**:Dalvik虚拟机提供了...

    android虚拟机分析

    Dalvik虚拟机中的垃圾回收器被设计为可以高效地处理频繁的对象创建和销毁,减少内存碎片的产生,优化内存使用。 在进行Dalvik虚拟机分析时,可以使用现有的多种工具来剖析和调试应用程序。例如,可以使用Android ...

Global site tag (gtag.js) - Google Analytics