博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java-GC-标记清除算法
阅读量:6840 次
发布时间:2019-06-26

本文共 2978 字,大约阅读时间需要 9 分钟。

## 前置知识

  1. 静态变量在类被加载的时候分配内存.当我们启动一个App的时候,系统会创建一个进程,此进程会加载一个JVM的实例,然后代码就运行在JVM之上.也就是说类在被加载的时候,静态变量

---

判断一个对象是否有垃圾


判断一个对象是否存在垃圾需要回收,目前有两种算法.

引用计数法


Java是通过引用去和对象进行关联的,如果要操作对象,则必须通过引用去完成.这样就有一个简单粗暴的方法可以去判断某个对象是否是"垃圾",如果一个对象没有任何的引用与之关联,则该对象无法在其他地方被用到,那么该对象则可以作为被回收对象. 此种方法为引用计数法,虽然这样简单粗暴,效率奇高,但是也存在着知名的问题,如果存在某些对象被循环引用,则该算法无法成功进行垃圾回收.


代码如下:
public class test {    public test instance;    public static void main(String[] args) {        test a = new test();        test b = new test();        a.instance = b;        b.instance = a;        a = null;        b = null;    }}

> 虽然变量a和变量b已经赋值为null,但是在堆内存中a的instance指向b,b的instance指向a, 这样依然存在着引用,所以引用计数法无法解决该问题.

### 可达算法

GC roots:

  1. 虚拟机栈中引用的对象,每个方法执行的时候,jvm都会创建一个相应的栈帧(操作数栈,局部变量表,运行时常量池),栈帧中包含着该方法内部使用的所有对象的引用(当然也包括其他的一些基本数据类型),当方法执行完毕的时候,该栈帧会从虚拟机栈中弹出,这样临时创建的对象的引用也就不在了,或者说没有任何GC roots指向这些对象,这些对象会在下一次的 垃圾回收时 被回收掉.

  2. 方法区中类静态属性引用的对象,静态属性是该类的属性,不属于任何的单独实例,是被该类下所有对象共有的,因此该属性自然会作为GC roots只要该类存在,该引用指向的对象也会一直存在,类也是会被回收的,后面有.

  3. 本地方法栈引用的对象.

  4. 软引用(softReference)将在内存溢出的时候,被判定为可回收对象.弱引用(weakRederence)会在下一轮的内存回收中作为可回收对象.

    ---

目前垃圾回收算法有四种:标记清除法,标记压缩法,复制法,引用计数法.目前流行的垃圾回收算法一般是上面集中算法的取长补短.


标记清除法

mutator:

  1. 垃圾收集器之外的部分,其职责一般是new,read,write.
  2. mutator roots,mutator根对象一般指的是分配在堆内存之外,可以直接被mutator直接访问到的对象,一般是指静态/全局变量以及thread local变量.

collector:

  1. 垃圾收集器,回收不再使用的内存以供mutator继续new.

可达对象:

  1. 从mutator根对象开始进行遍历,可以被访问到的对象都是可达对象,这些对象也是mutator(目前运行的应用程序)正在使用的对象.

标记-清除

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

这样就完成了标记的工作.

  • 清除阶段 : collector从堆内存从头至尾开始线性遍历,如果发现某个对象没有被标记为可达对象,则将其回收.
    图片丢了?

标记阶段mutator有1,2,3. 其中1可以到达B,B可以到达E 则B,E为可达对象,同理得其他可达对象.

> 清除阶段所有的非可达对象都会被回收.同时collector在进行标记和清除的阶段因为设计到修改和访问内存,所以需要暂停整个应用程序,等待标记清除结束之后回复程序运行状态. (Stop The World ???)

---

下面附上垃圾回收的伪代码:

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

下面是对应的标记算法:

markFromRoots():    worklist <- empty    foreach 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          foreach fld in Pointers(ref)  //遍历ref对象的所有指针域,如果其指针域(child)是可达的,直接标记其为可达对象并且将其加入worklist中          //通过这样的方式来实现深度遍历,直到将该对象下面所有可以访问到的对象都标记为可达对象。                child <- *fld                if child != null && isNotMarked(child)                   setMarked(child)                   add(worklist,child)

下面附上清除伪代码:

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

---


缺点: 标记清除算法比较大的缺点就是垃圾收集后可能会造成大量的内存碎片,如果需要超出碎片长度的连续空间的时候,可能会造成"Out of Memory"的情况.

转载于:https://www.cnblogs.com/A-FM/p/10907605.html

你可能感兴趣的文章
jQuery中的动画
查看>>
在Linux服务器上添加ip白名单允许ssh登录访问
查看>>
JAVA入门到精通-第71讲-学生管理系统3-增删改查
查看>>
如何设置putty远程登录linux
查看>>
Mysql聚合函数
查看>>
React组件继承的由来
查看>>
当当网首页——JS代码
查看>>
java实现遍历树形菜单方法——service层
查看>>
ios appIcon配置
查看>>
首款移动社交购物平台“商宝”上线发布
查看>>
Solr配置与简单Demo[转]
查看>>
Go语言转义字符
查看>>
spring boot的热加载(hotswap)
查看>>
iis重启的几种方法
查看>>
HDU 2196 Computer 树形DP经典题
查看>>
HDU 3466 01背包变形
查看>>
用鼠标滚轮做事件
查看>>
python - paramiko模块 修改logging提示为静默
查看>>
浅谈Linux的内存管理机制
查看>>
Linux中断概述
查看>>