垃圾回收 -标记清除算法

这篇具有很好参考价值的文章主要介绍了垃圾回收 -标记清除算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

就如他的字面意思一样,由标记阶段和清除阶段构成。标记阶段是把所有的活动对象都做上标记的阶段。清除阶段是把那些没有标记的对象,也就是非活动对象回收的阶段。通过这两个阶段,就可以令不能利用的内存空间重新得到利用。

1、 标记阶段

mark_phase(){
	for(r:$roots)
		mark(*r)
}

在标记阶段中,collector会为堆里所有活动对象打上标记。为此,我们首先要标记通过根直接引用的对象。然后递归地标记通过指针数组能访问到的对象。这样就能把所有活动对象都标记上。

mark(obj){
	if(obj.mark == false)
		obj.mark = true
		for(child :children(obj))
			mark(*child)
}

如果标记未完成,则程序会在对象头部进行置位操作。这个位要分配在对象的头之中,并且能用obj.mark访问。
垃圾回收 -标记清除算法,垃圾回收,算法,java,开发语言

在标记阶段,程序会标记所有活动对象。毫无疑问,标记所花费的时间是与“活动对象的总数成正比”。
标记阶段结束时的堆如下图所示:

垃圾回收 -标记清除算法,垃圾回收,算法,java,开发语言

2、 清除阶段

在清除阶段中,collector会遍历整个堆,回收没有打上标记的对象(即垃圾),使其可以再次得到利用。

sweep_phase(){
	sweeping = $heap_start
	while(sweeping < $heap_end)
		if(sweeping.mark == true) sweeping,mark = false
		else
			sweeping.next = $free_list
			&free_list = sweeping
		sweeping += sweeping.size
}

在此出现了叫做size的域,这是存储对象大小(字节数)的域,跟mark域一样,我们事先在各对象的头中定义他们。
在清除阶段,我们使用变量sweeping遍历堆,具体来说就是从堆首地址$heap_start开始,按顺序一个个遍历对象的标志位。
设置了标志位,就说明这个对象是活动对象。活动对象必然是不能回收的。因为我们取消标志位,准备下一次的GC。
我们必须把非活动对象回收再利用。回收对象就是把对象分块,链接到被称为“空闲链表”的单向链表。在之后进行分配时只要遍历这个空闲链表,就可以找到分块了。
在清除阶段,程序会遍历所有堆,进行垃圾回收。也就是说,所花费时间与堆大小成正比。堆越大,清楚阶段所花费的时间就会越长。
垃圾回收 -标记清除算法,垃圾回收,算法,java,开发语言

3、分配

接下来为大家讲解分配的相关内容。这里的分配是指将回收的垃圾进行再利用。那么,分配是怎样进行的呢?也就是说,当mutator 申请分块时,怎样才能把大小合适的分块分配给mutator 呢?
如前文所述,我们在清除阶段已经把垃圾对象连接到空闲链表了。搜素空闲链表并寻找大小合适的分块,这项操作就叫作分配。执行分配的函数 new_obj()如下所示。

new_obj(size){
	chunk = pickup_chunk(size,$free_list)
	if(chunk != NULL) return chunk
	else allocation_fail()
}

这里的pickup_chunk()函数用于遍历$free_list,寻找大于等于size的分块。如果他找到和size大小相同的分块,则会直接返回改分块;如果找到比size大的分块,则会将其分割成size大小的分块和去掉size后剩余大小的分块,并把剩余的分块返回给空闲链表。

分配策略 描述
First-fit 发现大于等于size的分块立即返回结果
Worst-fit 找出大于等于size的最大分块返回结果
Best-fit 找出大于等于size的最小分块返回结果

4、合并

根据分配策略的不同可能会产生大量的小分块。但如果他们是连续的,我们就能把所有的小分块连在一起形成一个大分块。这种操作就叫做合并。

sweep_phase(){
	sweeping = $heap_start
	while(sweeping < $heap_end)
		if(sweeping.mark == true) sweeping.mark  = false
		else
			if(sweeping == $free_list + $free_list.size)
				$free_list.size += sweeping.size
			else
				sweeping.next = $free_list
				$free_list = sweeping
			sweeping += sweeping.size
}

5、优缺点

优点

  • 实现简单
  • 与保守式GC算法兼容:不会移动对象

缺点

  • 碎片化:使用过程中会逐渐产生被细化的分块,不久后就会导致无数的小分块散布在堆的各处。众所周知,Windows的文件系统也会产生这种现象。
  • 分配速度底下:因为分块不是连续的,每次分配都必须遍历空闲链表,找到足够大的分块。最糟糕的情况就是每次进行分配都得把空闲链表遍历到最后。下面将要介绍的多个空闲链表和BiBOP法都是为了能在标记清除算法中高速进行分配而想出的方法。
  • 与写时复制技术不兼容:即使没有重写对象,GC也会设置所有活动对象的标志位,这样就会频繁发生本不应该发生的复制,压迫到内存空间。

写时复制技术(copy-on-write)
在Linux等众多UNIX操作系统的虚拟存储中用的的高速化方法。比如:在Linux中复制进程,也就是使用fork函数时,大部分内存空间都不会被复制。只是复制进程,就复制了所有内存空间不太实际。因此写时复制技术只是装作复制了内存空间,实际上是将内存空间共享了。
在各个进程中访问数据时,能够访问共享内存就没什么问题了。
然而,当我们对共享内存空间进行写入时,不能重写共享内存。因为从其他程序访问时,会发生数据不一致的情况。在重写时,要复制自己私有空间的数据,对这个私有空间进行重写。复制后只访问这个私有空间,不访问贡献内存。像这样,因为这门技术是在写入时进行复制的,所以才称为写时复制技术。

6、多个空闲链表

之前的标记清除算法,我们只用到了一个空闲链表,在这个空闲链表中,对大的分块和小的分块进行了同样的处理,但这样一来,每次分配的时候都要遍历一次空闲链表来寻找合适大小的空闲链表。
因此,我们就利用分块大小不同的空闲链表,来分配不同的分块。
垃圾回收 -标记清除算法,垃圾回收,算法,java,开发语言

7、BiBOP法

Big Bag Of Page:将大小相近的对象整理成固定大小的块进行管理的做法。
我们把堆分割成固定大小的块,让每个块只能配置同样大小的对象。
垃圾回收 -标记清除算法,垃圾回收,算法,java,开发语言

BiBOP法原本是为了消除碎片化,提高堆效率而采用的方法,但像上面这样,在多个块中分散残留着同样大小的对象,反而会降低堆的使用效率。

8、位图标记

在单纯的标记清除法中,用于标记的位是被分配到各个对象的头中的,也就是说,算法是把对象和头一并处理的。所以这和写时复制技术不兼容。
对此我们有个方法,那就是只收集各个对象的标志位并表格化,不和对象一起管理,在标记的时候不在对象的头里置位,而是在这个表格中的特定场所置位。像这样集合了用于标记的位的表哥称为“位图表格”,利用这个表格进行标记的行为称为“位图标记”
垃圾回收 -标记清除算法,垃圾回收,算法,java,开发语言
这样做的优点就是与写时复制技术相兼容,不会产生无谓的复制。并且清除起来也更加高效。

9、延迟清除法

之前我们提到过,清除操作所花费的时间与堆大小成正比。也就是说堆越大,清除所花费的时间就越长。结果就会妨碍到mutator的处理。
延迟清除法,就是在标记操作结束后,不一并进行清除操作,而是延迟,来防止mutator长时间的暂停。
因为延迟清除法,不是一下遍历整个堆,他只在分配时执行必要的便利,所以可以压缩因清除操作导致的mutator的暂停时间。文章来源地址https://www.toymoban.com/news/detail-689990.html

到了这里,关于垃圾回收 -标记清除算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java-垃圾回收与算法

    1. 引用计数法   在 Java 中,引用和对象是有关联的。如果要操作对象则必须用引用进行。因此,很显然一个简单的办法是通过引用计数来判断一个对象是否可以回收。简单说,即一个对象如果没有任何与之关联的引用,即他们的引用计数都不为 0,则说明对象不太可能再被用

    2024年02月12日
    浏览(26)
  • 【Java】图解 JVM 垃圾回收(一):GC 判断策略、引用类型、垃圾回收算法

    垃圾 是指运行程序中 没有任何引用指向的对象 ,需要被回收。 内存溢出 :经过垃圾回收之后,内存仍旧无法存储新创建的对象,内存不够溢出。 内存泄漏 :又叫 “ 存储泄漏 ”,对象不会再被程序使用了,但是 GC 又不能回收它们。例如:IO 流不适用了但是没有被 Close、

    2024年02月19日
    浏览(37)
  • java中的垃圾回收算法

    java中有四种垃圾回收算法,分别是: 标记清除法、标记整理法、复制算法、分代收集算法 1、标记清除法: 第一步:利用可达性去遍历内存,把存活对象和垃圾对象进行标记; 第二步:在遍历一遍,将所有标记的对象回收掉; 特点:效率不行,标记和清除的效率都不高;标记和清除

    2024年02月15日
    浏览(27)
  • 初步认识Java垃圾回收算法

    GCRoot指被栈上直接或间接引用的对象,或被本地方法栈直接或间接引用的对象,或被方法区引用的对象。 被引用的对象是不能被删除的。 如果对象跟GCRoot并没有直接或间接相连的关系,那么这些对象就可以被删除了。 标记-清理 :将需要删除的对象标记一下,标记完了再扫

    2024年02月11日
    浏览(41)
  • java---垃圾回收算法(GC)

    目录 一、如何判断一个对象是否存活 1.引用计数法 2.可达性分析法 二、垃圾回收算法 1.标记清除法 2.复制算法 3.标记整理法 4.分代算法 具体流程 注意事项 空间分配担保原则 总结 Java 堆中存放着几乎所有的对象实例,垃圾回收器在对堆进行垃圾回收前,首先要判断这些对象

    2024年02月05日
    浏览(28)
  • 【Java虚拟机】JVM垃圾回收机制和常见回收算法原理

    1.垃圾回收机制 (1)什么是垃圾回收机制(Garbage Collection, 简称GC) 指自动管理动态分配的内存空间的机制,自动回收不再使用的内存,以避免内存泄漏和内存溢出的问题 最早是在1960年代提出的,程序员需要手动管理内存的分配和释放 这往往会导致内存泄漏和内存溢出等问

    2024年02月02日
    浏览(38)
  • Java垃圾回收-可达性分析算法,rabbitmq原理图

    虚拟机栈(栈帧中的本地变量表)中引用的对象。(可以理解为:引用栈帧中的本地变量表的所有对象) 方法区中静态属性引用的对象(可以理解为:引用方法区该静态属性的所有对象) 方法区中常量引用的对象(可以理解为:引用方法区中常量的所有对象) 本地方法栈中(Native方法)引用的

    2024年04月16日
    浏览(29)
  • JVM——垃圾回收(垃圾回收算法+分代垃圾回收+垃圾回收器)

    只要一个对象被其他对象所引用,就要让该对象的技术加1,某个对象不再引用其,则让它计数减1。当计数变为0时就可以作为垃圾被回收。 有一个弊端叫做循环引用,两个的引用计数都是1,导致不能作为垃圾回收,会造成内存泄露。 java虚拟机没有采用该算法。 该算法需要

    2024年02月12日
    浏览(35)
  • 【小程序分享篇 一 】开发了个JAVA小程序, 用于清除内存卡或者U盘里的垃圾文件非常有用

    有一种场景, 手机内存卡空间被用光了,但又不知道哪个文件占用了太大,一个个文件夹去找又太麻烦,所以我开发了个小程序把手机所有文件(包括路径下所有层次子文件夹下的文件)进行一个排序,这样你就可以找出哪个文件占用了内存太大了。 使用例子如下,用JAVA

    2023年04月21日
    浏览(32)
  • JVM7:垃圾回收是什么?从运行时数据区看垃圾回收到底回收哪块区域?垃圾回收如何去回收?垃圾回收策略,引用计数算法及循环引用问题,可达性分析算法

    在Java中,垃圾回收(Garbage Collection,简称GC),是自动管理内存的机制。它负责检测不再使用的对象,并释放它们所占用的内存,以供其他对象使用。 JVM内存模型认识的差不多了,就应该思考,什么样的内存模型适合什么样的GC策略,包括垃圾回收为什么会出现。实际上,很多

    2024年02月11日
    浏览(25)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包