GC垃圾回收算法

什么是GC垃圾回收呢。日常生活中我们去餐厅吃饭吃完饭,吃完饭走了餐具不用管,服务员在把餐具拿走,这是一种方式,服务员怎么知道他要来把餐具拿走呢,因为你走了,这个位置空了。服务员什么时候拿走餐具很重要,第一你没吃完,不会回收吧,第二很多人吃饭,你在里面,他也不一定回收吧,他会找一个合适的时机,一次性回收很多吧。

第二种方法:自己吃完饭把餐具带到回收的地方吧。

哪一种方式更好呢?

第一种方式自己也就是使用者不用管,服务员找一个合适的时机把垃圾回收了。

第二种自己去吧垃圾倒了。

在java中GC垃圾回收用的是第一种方式,系统找一个合适的时机把垃圾回收掉,我们不用手动去干预。这也是C#使用的垃圾回收机制。开发员人员只关心内存申请,不关心内存释放的问题

下面我们看一看垃圾回收GC回收算法。

 

1.1. 引用计数法

引用计数法是一种古老的垃圾收集方法,引用计数器实现很简单,对于一个对象A,有任何一个对象引用了A,那么A的计数器+1,引用失效时,A的计数器-1。当A的引用计数器是0的时候。那么A对象就不能被使用了。

引用计数法实现很简单就是额外的为每一个对象搞一个计数器,但是也有缺点:

1.引入计数器是解决一个问题又附加的引入一个问题。因为每次加减操作,可能影响系统性能。

2.无法处理循环引用问题,因此java的垃圾回收器中,没有使用这种算法。

 

下面举一个简单的例子:A对象和B对象,对象A中有B的引用,对象B中有A的引用。此时A和B两个对象的计数器都不是0,但在使用中没有其他对象引用A和B,说白了,A和B应该是被回收的对象,但是他们之间相互引用。计数器不为0,系统没有这种探针检测到循环引用,从而无法垃圾回收,引发内存泄露。

解释一个可达对象,不可达对象。

可达对象:通过根节点进行引用搜索,最终可以到达的对象。

不可达对象:通过根节点进行引用搜索,最终没有被引用的对象。

不可达的对象出现循环引用,它的引用计数器都不是0如下图所示:

 

 

1.2. 标记清除法

标记清除算法将垃圾的回收两阶段进行:标记阶段和清除阶段。

在标记阶段,顾名思义,就是从根节点开始标记可到达的对象。没有被标记的对象也就是没有被引用的对象那个就是垃圾对象。在第二个阶段也就是清除阶段,清除所有没有被标记的对象。标记清除算法。可能会产生空间的碎片,这也是这个算法的最大的问题。

如下图所示,使用标记清除算法对一块连续的内存空间进行回收,从根节点开始,所有的引用关系对象被标记为存活对象,

不可到达的对象被标记为垃圾对象,在清除阶段回收所有不可到达的对象,也就是没有被引用的对象。

 

如上图所示,回收后的空间是不连续的。在对象的堆空间分配中,大对象的内存分配,不连续的内存空间的效率低于连续的空间。这也是该算法的最大缺点。

标记阶段通过根节点查找所有可达对象,清除节点清除所有的不可达对象。完成系统的垃圾回收。

1.3. 复制算法

复制算法的原理:将分配的内存空间分为2块,每次只是用1块,在垃圾回收呢时,讲正在使用的内存中的存活对象复制到没有被使用的内存的一块,完成之后,清除正在使用的内存块中的所有对象,然后两个内存块交换,以此完成垃圾回收。

如果系统中的垃圾对象有很多,存活对象比较少,复制算法的优势就体现出来了。由于对象是在垃圾回收过程中统一被复制到新的内存空间,所以复制算法可以确保新的内存空间是没有碎片的。但是缺点就是,把内存分为了2块,不能充分的利用内存。

如下图所示:A、B两块相同的内存,A在进行垃圾回收的时候,讲存货的对象复制到B的空间,B的空间在复制后是连续的。复制完成之后,清除A。把B设置为当前使用的空间。

 

在java的新生代串行垃圾回收中,使用了复制算法,新生代之前的章节也讲过分为eden区域,from、to三个部分,之前也说过from、to是相等的可以替换的两个空间、地位相等、角色相等。from、to空间也被称之为survivor空间,英文解释为幸存者,用来存放没有被回收的对象,如下图所示。

 

新生代:堆空间中的年轻人,对象刚刚创建或者经历的垃圾回收次数不多的对象。

老年代:堆空间中的老年人,老年对象指经历了很多次垃圾回收亦然存活的对象,(看来大部分老年人存贮内存挺高的对象)。

在垃圾回收的过程中,eden空间存活的对象被复制到survivor空间,假如我们正在使用from空间,因此den空间存活的对象被复制到to空间。如果to空间内存满了,则对象直接进入老年代,可参考虚拟机参数设置的章节参考实例。此时eden空间和from空间剩余的对象就是垃圾对象,直接清空,to空间存放的就是存活的对象,这种改进之后的算法,保证了空间的连续性,这点很重要,又避免了大量的空间的浪费,因为这些空间是可以手动分配的。参考之前的章节。上图显示了复制算法的时机回收过程。

复制算法比较适合新生代,因为新生代垃圾对象一般比存活对象要多,这块区域,朝生夕死足矣。如果这个地方的对象存活就可能是常住内存的对象了。

 

1.4. 标记压缩法

上面也说了 压缩算法的使用前提是存活对象少、垃圾对象多的情况下使用。这种情况经常发生在新生代,在老年到就不合适了,因为老年代大部分都是存活对象,如果要使用复制算法,复制的成本很高,所以老年代使用的是其他的算法,老年人比较特殊嘛。

标记压缩算法是在标记清除算法基础之上优化了,标记压缩算法也是从根节点开始,对对象的可达性做一次扫描标记,之后不是直接清除未标记的对象,而是将所有的存活对象压缩到内存的一端,之后,清除边界外的所有空间,这样做的好处是避免了内存的不连续性,不需要内存设置两块相同的内存进行复制交换。所以压缩标记算法的性价比好啊。

标记压缩算法如下图所示完成垃圾回收。

 

标记压缩算法其实就是在标记清除算法完成之后,进行一次随便整理。所以称之为标记压缩算法。

 

1.5. 分区算法

上面说的都是出来堆空间的算法,对于栈是如何处理的呢。分区算法是将整个堆空间分成连续的不同的小区间,如下图所示。每一个小区间都是独立使用,独立回收,这样设计可以控制一次回收多少个区间。不回去全扫描。

 

一般来说在相同的条件下,堆空间越大,一次垃圾GC回收所需要的时间就越长,那么引起的副作用就是停顿时间越长,就是假死需要等待的时间就越长,使用分区算法的好处就是,合理的回收区间,不是扫描整个堆空间,从而减少一次GC产生的停顿。跟餐厅收餐具一样一样,分房间去收餐具,不是每次都按照顺序收餐具。

时间: 2024-09-25 02:35:35

GC垃圾回收算法的相关文章

浅谈PHP 5中垃圾回收算法的演化

PHP是一门托管型语言,在PHP编程中程序员不需要手工处理内存资源的分配与释放(使用C编写PHP或Zend扩展除外),这就意味着PHP本身实现了垃圾回收机制(Garbage Collection).现在如果去PHP官方网站(php.net)可以看到,目前PHP5的两个分支版本PHP5.2和PHP5.3是分别更新的,这是因为许多项目仍然使用5.2版本的PHP,而5.3版本对5.2并不是完全兼容.PHP5.3在PHP5.2的基础上做了诸多改进,其中垃圾回收算法就属于一个比较大的改变.本文将分别讨论P

浅谈PHP5中垃圾回收算法(Garbage Collection)的演化

前言:PHP是一门托管型语言,在PHP编程中程序员不需要手工处理内存资源的分配与释放(使用C编写PHP或Zend扩展除外),这就意味着PHP本身实现了垃圾回收机制(Garbage Collection).现在如果去PHP官方网站(php.net)可以看到,目前PHP5的两个分支版本PHP5.2和PHP5.3是分别更新的,这是因为许多项目仍然使用5.2版本的PHP,而5.3版本对5.2并不是完全兼容.PHP5.3在PHP5.2的基础上做了诸多改进,其中垃圾回收算法就属于一个比较大的改变.本文将分别

JVM调优总结(九)-新一代的垃圾回收算法

垃圾回收的瓶颈 传统分代垃圾回收方式,已经在一定程度上把垃圾回收给应用带来的负担降到了最小,把应用的吞吐量推到了一个极限.但是他无法解 决的一个问题,就是Full GC所带来的应用暂停.在一些对实时性要求很高的应用场景下,GC暂停所带来的请求堆积和请求失败是无法接受 的.这类应用可能要求请求的返回时间在几百甚至几十毫秒以内,如果分代垃圾回收方式要达到这个指标,只能把最大堆的设置限制在一个 相对较小范围内,但是这样有限制了应用本身的处理能力,同样也是不可接收的. 分代垃圾回收方式确实也考虑了实时性

深入理解JVM之三:垃圾回收算法

前言 垃圾收集算法是JVM中垃圾收集器的方法论,所以了解算法是必要的,在算法领域只做最简单的介绍,力求文章的简单易懂.垃圾收集算法主要有以下几种:标记-清除算法(mark-sweep).复制算法(copying)和标记-整理算法(mark-compact).随着jdk版本的升级,垃圾收集器也在不断的升级,现在最新的垃圾收集器已经能够对Java堆中一部分进行回收,也能够对Java堆中另一部分进行回收,这一成果在jdk1.7中得到体现.但是垃圾收集器的底层算法是深入垃圾收集器所必须了解的,这篇文章将

Go 语言的垃圾回收算法被吹过头?与Java比如何?

Go语言正在构建的垃圾收集器(GC),似乎并不像宣传中那样的,技术上迎来了巨大突破.那么,与Java语言作对比之后,该怎么选择呢? 写在前面 最近,我读到一些大肆宣传Go语言最新垃圾回收器的文章,这些文章对垃圾回收器的描述让我感到有些厌烦.这些文章有些是来自Go项目.他们宣称GC技术正迎来巨大突破. 下面Go团队在2015年8月发布的新垃圾回收器的启动声明:https://blog.golang.org/go15gc Go正在构建一个划时代垃圾回收器,2015年,甚至到2025年,或者更久--G

JVM调优总结:新一代的垃圾回收算法

垃圾回收的瓶颈 传统分代垃圾回收方式,已经在一定程度上把垃圾回收给应用带来的负担降到了最小,把应用的吞吐量推到了一个极限.但是他无法解决的一个问题,就是Full GC所带来的应用暂停.在一些对实时性要求很高的应用场景下,GC暂停所带来的请求堆积和请求失败是无法接受的.这类应用可能要求请求的返回时间在几百甚至几十毫秒以内,如果分代垃圾回收方式要达到这个指标,只能把最大堆的设置限制在一个相对较小范围内,但是这样有限制了应用本身的处理能力,同样也是不可接收的. 分代垃圾回收方式确实也考虑了实时性要求而

JVM内存模型及垃圾回收算法

原文地址: http://blog.csdn.net/kingofworld/article/details/17718587   JVM内存模型总体架构图 程序计数器多线程时,当线程数超过CPU数量或CPU内核数量,线程之间就要根据时间片轮询抢夺CPU时间资源.因此每个线程有要有一个独立的程序计数器,记录下一条要运行的指令.线程私有的内存区域.如果执行的是JAVA方法,计数器记录正在执行的java字节码地址,如果执行的是native方法,则计数器为空.虚拟机栈线程私有的,与线程在同一时间创建.

推荐!可视化垃圾回收算法(转)

英文原文:Visualizing Garbage Collection Algorithms 大部分开发者都认为自动垃圾回收器是理所当然的.实际上,这只是语言运行时提供的一项实用功能,旨在简化我们的开发工作. 但是如果尝试着了解垃圾回收器的内部原理,你会发现很难弄明白.除非熟悉它的工作流程和错误处理方式,否则内部成千上万的实现细节会让你不知所措. 我编译了一个有五种不同的垃圾回收算法工具.程序运行时会创建一个动画界面.你可以从github.com/kenfox/gc-viz上获取动画和代码来实现

(5)jvm垃圾回收器相关垃圾回收算法

引用计数法[原理]--->引用计数器是经典的也是最古老的垃圾收集防范.--->实现原理:对于对象A,只要有任何一个对象引用A,则计数器加1.当引用失效时,计数器减1.只要对象A的计数器值为0时,则A的为垃圾.--->引用计数器法存在两个缺陷:        (1)无法处理循环引用的情况.A中引用B,B中引用A.无第三方对象引用A和B.则A和B为垃圾,但A和B的计数器不为0.        (2)引用计数器要求在每次因引用产生和消除的时候,需要伴随一个加法操作和减法操作,对系统性能会有一定