前言

垃圾自动回收机制的出现使编程更加的简单,使得我们不需要再去考虑内存分配和释放的问题,而是更加的专注在我们产品功能的实现上。但是我们还是需要花时间去了解下垃圾收集机制是怎么工作的,以便后面能够更好的进行我们应用的性能调优等。

目前最基本的垃圾收集算法有四种,标记-清除算法(mark-sweep),标记-压缩算法(mark-compact),复制算法(copying)以及引用计数算法(reference counting).而现代流行的垃圾收集算法一般是由这四种中的其中几种算法相互组合而成,比如说,对堆(heap)的一部分采用标记-清除算法,对堆(heap)的另外一部分则采用复制算法等等。今天我们主要来看下标记-清除算法的原理。

基本概念

在了解标记-清除算法前,我们先要了解几个基本概念。

首先是mutator和collector,这两个名词经常在垃圾收集算法中出现,collector指的就是垃圾收集器,而mutator是指除了垃圾收集器之外的部分,比如说我们应用程序本身。mutator的职责一般是NEW(分配内存),READ(从内存中读取内容),WRITE(将内容写入内存),而collector则就是回收不再使用的内存来供mutator进行NEW操作的使用。

第二个基本概念是关于mutator roots(mutator根对象),mutator根对象一般指的是分配在堆内存之外,可以直接被mutator直接访问到的对象,一般是指静态/全局变量以及Thread-Local变量(在Java中,存储在java.lang.ThreadLocal中的变量和分配在栈上的变量 - 方法内部的临时变量等都属于此类).

第三个基本概念是关于可达对象的定义,从mutator根对象开始进行遍历,可以被访问到的对象都称为是可达对象。这些对象也是mutator(你的应用程序)正在使用的对象。

算法原理

顾名思义,标记-清除算法分为两个阶段,标记(mark)和清除(sweep).

在标记阶段,collector从mutator根对象开始进行遍历,对从mutator根对象可以访问到的对象都打上一个标识,一般是在对象的header中,将其记录为可达对象。

而在清除阶段,collector对堆内存(heap memory)从头到尾进行线性的遍历,如果发现某个对象没有标记为可达对象-通过读取对象的header信息,则就将其回收。

从上图我们可以看到,在Mark阶段,从根对象1可以访问到B对象,从B对象又可以访问到E对象,所以B,E对象都是可达的。同理,F,G,J,K也都是可达对象。到了Sweep阶段,所有非可达对象都会被collector回收。同时,Collector在进行标记和清除阶段时会将整个应用程序暂停(mutator),等待标记清除结束后才会恢复应用程序的运行,这也是Stop-The-World这个单词的来历。

接着我们先看下一般垃圾收集动作是怎么被触发的,下面是mutator进行NEW操作的伪代码:

New():
    ref <- allocate()  //分配新的内存到ref指针
    if ref == null
       collect()  //内存不足,则触发垃圾收集
       ref <- allocate()
       if ref == null
          throw "Out of Memory"   //垃圾收集后仍然内存不足,则抛出Out of Memory错误
          return ref atomic collect():
    markFromRoots()
    sweep(HeapStart,HeapEnd)

而下面是对应的mark算法:

markFromRoots():
    worklist <- empty
    for each fld in Roots  //遍历所有mutator根对象
        ref <- *fld
        if ref != null && isNotMarked(ref)  //如果它是可达的而且没有被标记的,直接标记该对象并将其加到worklist中
           setMarked(ref)
           add(worklist,ref)
           mark()
mark():
    while not isEmpty(worklist)
          ref <- remove(worklist)  //将worklist的最后一个元素弹出,赋值给ref
          for each fld in Pointers(ref)  //遍历ref对象的所有指针域,如果其指针域(child)是可达的,直接标记其为可达对象并且将其加入worklist中
          //通过这样的方式来实现深度遍历,直到将该对象下面所有可以访问到的对象都标记为可达对象。
                child <- *fld
                if child != null && isNotMarked(child)
                   setMarked(child)
                   add(worklist,child)

在mark阶段结束后,sweep算法就比较简单了,它就是从堆内存起始位置开始,线性遍历所有对象直到堆内存末尾,如果该对象是可达对象的(在mark阶段被标记过的),那就直接去除标记位(为下一次的mark做准备),如果该对象是不可达的,直接释放内存。

sweep(start,end):
    scan <- start
   while scan < end
       if isMarked(scan)
          setUnMarked(scan)
      else
          free(scan)
      scan <- nextObject(scan)

缺点

标记-清除算法的比较大的缺点就是垃圾收集后有可能会造成大量的内存碎片,像上面的图片所示,垃圾收集后内存中存在三个内存碎片,假设一个方格代表1个单位的内存,如果有一个对象需要占用3个内存单位的话,那么就会导致Mutator一直处于暂停状态,而Collector一直在尝试进行垃圾收集,直到Out of Memory。

JVM--标记-清除算法Mark-Sweep的更多相关文章

  1. 1. GC标记-清除算法(Mark Sweep GC)

    世界上第一个GC算法,由 JohnMcCarthy 在1960年发布. 标记-清除算法由标记阶段和清除阶段构成. 标记阶段就是把所有的活动对象都做上标记的阶段. 标记阶段就是"遍历对象并标记 ...

  2. (转)jvm具体gc算法介绍标记整理--标记清除算法

    转自:https://www.cnblogs.com/ityouknow/p/5614961.html GC算法 垃圾收集器 概述 垃圾收集 Garbage Collection 通常被称为“GC”, ...

  3. JVM内存管理------GC算法精解(五分钟让你彻底明白标记/清除算法)

    相信不少猿友看到标题就认为LZ是标题党了,不过既然您已经被LZ忽悠进来了,那就好好的享受一顿算法大餐吧.不过LZ丑话说前面哦,这篇文章应该能让各位彻底理解标记/清除算法,不过倘若各位猿友不能在五分钟内 ...

  4. JVM之GC算法、垃圾收集算法——标记-清除算法、复制算法、标记-整理算法、分代收集算法

    标记-清除算法 此垃圾收集算法分为“标记”和“清除”两个阶段: 首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记对象,它的标记过程前面已经说过——如何判断对象是否存活/死去 死去的对象就会 ...

  5. JVM内存管理之GC算法精解(五分钟让你彻底明白标记/清除算法)

    相信不少猿友看到标题就认为LZ是标题党了,不过既然您已经被LZ忽悠进来了,那就好好的享受一顿算法大餐吧.不过LZ丑话说前面哦,这篇文章应该能让各位彻底理解标记/清除算法,不过倘若各位猿友不能在五分钟内 ...

  6. GC算法精解(五分钟让你彻底明白标记/清除算法)

    GC算法精解(五分钟让你彻底明白标记/清除算法) 相信不少猿友看到标题就认为LZ是标题党了,不过既然您已经被LZ忽悠进来了,那就好好的享受一顿算法大餐吧.不过LZ丑话说前面哦,这篇文章应该能让各位彻底 ...

  7. Java GC 标记/清除算法

    1) 标记/清除算法是怎么来的? 我们在程序运行期间如果想进行垃圾回收,就必须让GC线程与程序当中的线程互相配合,才能在不影响程序运行的前提下,顺利的将垃圾进行回收. 为了达到这个目的,标记/清除算法 ...

  8. JVM-GC算法(一)-标记清除算法

    首先,我们得知道根搜索算法,它可以解决我们应该回收哪些对象的问题,但是它显然还不能承担垃圾搜集的重任,因为我们在程序(程序也就是指我们运行在JVM上的JAVA程序)运行期间如果想进行垃圾回收,就必须让 ...

  9. 《垃圾回收的算法与实现》——GC标记-清除算法

    基本算法 标记-清除算法由 ==标记阶段== 和 ==清除阶段== 构成. 标记即将所有活动的对象打上标记. 清除即将那些没有标记的对象进行回收. 标记与清除 遍历GC root引用,递归标记(设置对 ...

  10. CMS为什么采用“标记-清除”算法

    分代式GC里,年老代常用mark-sweep:或者是mark-sweep/mark-compact的混合方式,一般情况下用mark-sweep,统计估算碎片量达到一定程度时用mark-compact. ...

随机推荐

  1. 探索软件工程道路上的我II (Θ∀Θ#)

    ------作业要求------ 第一版本程序Prog1:+ 给定一个数组,实现数组元素求和:具体要求:实现对一维数组(a[100])的所有元素相加运算.+ 数据准备:a)数组长度:100:b)数组数 ...

  2. ASP.NET项目部署到Linux服务器出现服务器错误

    在Linux系统中安装了Mono和Apache作为Web服务器,使用Visual Studio开发的ASP.NET Web应用或者API应用,在部署到Linux服务器后出现服务器错误,其中一个原因是由 ...

  3. (转)如何处理iOS中照片的方向

    如何处理iOS中照片的方向 31 May 2015 • 7 min. read • Comments 使用过iPhone或者iPad的朋友在拍照时不知是否遇到过这样的问题,将设备中的照片导出到Wind ...

  4. div内容滚动,无缝滚动

    function startmarquee(lh, speed, delay) {        var t;        var oHeight = 373; /** div的高度 **/     ...

  5. 在windows下配置wnmp

    1.下载mysql 2.下载php 3.下载nginx 4.创建文件夹wnmp 把php,nginx,mysql放到wnmp目录下,另外新建www目录, 用于存放web文件 配置nginx,如下(ng ...

  6. Azure 媒体服务可将优质内容传输至 Apple TV

    作为内容提供商,如果想要将优质内容传输到Apple TV,需要使用Apple FairPlay Streaming (FPS)技术. 但是这个技术的构建比较繁琐,基于此,Azure提供了FairPla ...

  7. The Non-Inverting Amplifier Output Resistance by Adrian S. Nastase [ Copied ]

    Source Address: http://masteringelectronicsdesign.com/the-non-inverting-amplifier-output-resistance/ ...

  8. akka优势

    1.提供可扩展的实时事务处理. 2.为以下目标设计: 垂直扩展(并发) 水平扩展(远程调用) 高容错 3.Akka的核心,Akka-actor非常小的,可以非常方便地放进你的应用中,提供你需要的异步无 ...

  9. js MATH

    MATH Math 对象用于执行数学任务. 无需创建它,通过把 Math 作为对象使用就可以调用其所有属性和方法. Math 对象属性 var a=Math.E; //输出2.718281828459 ...

  10. PHP 登录

    <?php// 本类由系统自动生成,仅供测试用途namespace Home\Controller;use Think\Controller;use Org\Util\Rbac; use Thi ...