java垃圾回收概念以及垃圾回收算法,垃圾收集器

来源:互联网 发布:me直播软件 编辑:程序博客网 时间:2024/06/07 01:30

最近看了两本关于JVM的书,今天刚好有空就给大家总结一下关于jvm垃圾回收的一些知识点

GC中的垃圾,特指存在于内存中的不会被再次使用的对象。

常用的垃圾回收算法:

引用计数法:

引用计数器的实现非常简单,对于一个对象A,,只要任何一个对象引用了A,则A的计数器就加1,当引用失效时,计数器就减1.只要对象A的引用计数器的值为0,则对象A就不可能再被使用。

其实现也特别简单,只需要为每个对象配备一个整型计数器即可。但是引用计数器有两个严重的问题。

(1)      无法处理循环引用的情况。两个不可达对象之间的循环引用。

(2)      在每次因引用产生和消除的时候,需要伴随一个加法操作和减法操作。对系统性能有一定的影响。

标记清除法(Mark-Sweep)

标记清楚算法将垃圾回收分成两个阶段:标记阶段和清除阶段。一种颗星的实现是,在标记阶段,首先通过根节点,标记所有从跟节点开始的可达对象。因此没有被标记的对象就是未被引用的垃圾对象。然后在清除阶段,清楚所有未被标记的对象。标记清楚算法是最大的问题就是,产生空间碎片。

复制算法

一般用于存活对象少、垃圾对象多的情况,一般是新生代。圾回收时,将正在使用的内存中的存活对象复制到没有被使用的内存块中,之后清楚当前正在使用的内存块中的所有对象,交换两个内存对象的角色,完成垃圾回收。

代价:内存折半

标记压缩算法

标记压缩算法是一种老年代的回算法。它在标记清除算法的基础上做了一些优化。和标记清楚算法一样,标记压缩算法也需要从根节点开始,对所有可达节点做一次标记,将所有存活对象压缩到内存的另外一端,并保持它们之间的引用关系。这样既避免了内存碎片,又不需要两块相同的内存空间。

一般老年代的垃圾回收需要时间比较长是因为,老年代中存活对象比较多,而使用标记压缩算法需要将所有的存活对象压缩到内存的一端,

分代算法

分代算法将内存区间根据对象的特点分成几块,根据每块内存区间的特点,使用不同的算法,以提高垃圾回收的效率。

一般来说新生代主要使用复制算法,老年代使用标记清除算法或者标记压缩算法。

卡表:

对于新生代和老年代,通常,新生代回收的频率很高,但是每次回收的耗时都很短,而老年代回收的的频率比较低,但是会消耗更多的时间。为了支持高频率的新生代回收,虚拟机可能使用一种叫做卡表(card table)的数据结构。卡表为一个比特位集合,每一个比特位可以用来表示老年代的某一个区域中的所有对象是否持有新生代对象的引用。这样在新生代GC的时候可以不用花大量时间扫描所有老年代对象,来确定每一个对象的引用关系,而可以先扫描卡表,只有当卡表的标记位为1的时候,才需要扫描给定区域的老年对象。

分区算法

分区算法将按照对象的生命周期长短划分成两个部分,分区算法将整个堆空间划分成连续的不同的小空间。每一个小区间都独立使用,独立回收。这种算法的好处是可以控制一次回收多少个小区间。

一般来说相同条件下,堆空间越大,一次GC时所需要的时间就越长,从而产生的停顿也越长,为了更好的控制GC所产生的停顿时间,将一块大的内存区域分割成多个小块,根据目标的停顿时间,每次合理地回收若干个小区间,而不是整个堆空间,从而减少一次GC所产生的停顿。

 

判断可触及性

(1)      可触及的:从根节点开始,可以到达这个对象

(2)      可复活的,Java提供finalize()方法,垃圾回收器准备释放内存的时候,会先调用finalize(),对象有可能在该函数中被复活

(3)      不可触及的:对象在finilize()函数被调用,并且没有被复活,那么就会进入不可触及状态,不可触及状态的对象不可能被复活,因为finilize()函数只会被调用一次。

垃圾收集器:

串行回收:serial收集器

Serial收集器作用域新生代中,采用复制算法、串行回收和 Stop-the-World机制的方式执行内存回收。Serial收集器缺省也作为HotSpot中Client模式下的新生代垃圾收集器

Serial收集器还提供用于执行老年代垃圾收集的serialold 收集器。Serial old收集器同样也采用串行回收和stop-th-world机制,只不过内存回收算法采用的是标记-压缩算法。

 

优点:如果是单个CPU的宿主环境中,使用serial收集器+serial old收集器的组合执行Client模式下的内存回收将会是不错的选择。基于串行回收的垃圾收集器适用于大多数对暂停时间要求不高的Client模式下的JVM,CPU不需要频繁地做任务切换,因此可以有效避免多线程交互过程中产生的一些额外开销。

 

缺点:降低程序的吞吐量

并行回收:ParNew收集器

是serial收集器的多线程版本

ParNew收集器在新生代中采用并行回收、复制算法和stop-the-world机制

优点:在多CPU的宿主环境下,可以充分利用多CPU、多核心等物理硬件资源优势、可以迅速的完成垃圾回收,提升程序的吞吐量。

程序吞吐量优先:Parallel收集器

和Parnew收集器一样,在新生代中采用并行回收、复制算法和stop-the-world机制

Parallel收集器的主要优势是可以控制程序的吞吐量大小,因此它可以被为吞吐量优先的垃圾收集器。

Parallel old收集器采用并行回收、标记-压缩算法和stop-the-world机制,Parallel收集器+parallel  old收集器的组合执行Server模式下的内存回收将会是不错的选择。

低延迟:CMC(Concurrent-Mark-Sweep)收集器

基于低延迟的考虑,JVM的设计者们提供了基于并行回收的CMS收集器,它是一款优秀的老年代垃圾收集器。CMS天生为并发而生,低延迟是它的优势,采用标记-清楚算法,并且也会因为stop-the-world机制而出现短暂的暂停。

四个阶段:

初始标记阶段----标记是否可达,stop-the-world

并发标记阶段-----将之前不可达的对象标记为垃圾对象

再次标记阶段------由于上一阶段标记过程中,程序的工作线程会和垃圾收集器的线程同时运

行或者交叉运行,因此在并发标记阶段无法保证之前被标记为垃圾的无用对象的引用关系遭到更改,因此再次标记阶段会采用stop-the-world机制而再次标记,系统也因此会再次出现短暂的暂停,以确保所有的垃圾对象都被成功且正确的标记。

并发清除阶段:执行内存回收,释放掉无用对象所占用的内存空间。

区域化分代式:G1(Garbage-First)收集器

G1是一款基于并行和并发,低延迟,以及暂停时间更加可控的区域区域划分式垃圾收集器。

其革命性意义:

在空间设计上,G1重新塑造了整个java堆区,虽然也是基于分代的概念执行内存分配和垃圾回收,但是G1并没有采用传统物理隔离的新生代和老年代布局方式(仅在逻辑上划分为新生代和老年代),而是将java堆区划分成约2048个大小相同的独立region块。每个region块之间是不连续的,,其大小根据堆空间的实际大小划分,整体被控制在1MB到32MB之间,这样划分的好处在于可以更好的提升GC的回收效率,和缩短stop-the-world机制的暂停时间以获取更大的程序吞吐量,甚至能够真正做到精确控制程序的的暂停时间等。由于G1收集器并非全堆扫描,只优于回收占用内存较大的一些region块,所以G1收集器的暂停时间会更加可控。

G1收集器的执行过程主要可以划分为6个阶段:
初始标记阶段:stop-the-world,标记root-region

根区域扫描阶段:扫描root-region中引用老年代的一些region块

并发标记阶段:主要任务是找出整个java堆区中的存活对象,由于该阶段并不会导致程序出现暂停,因此在执行的过程中允许被新生代内存回收打断

再次标记阶段:和初始标记阶段同样都是基于stop-the-world机制的,该阶段的只要任务就是完成整个堆区中存活对象的标记,

清除阶段:由三部分构成,首先会计算出所有的活跃对象并完全释放一些自由度region块,然后处理Remember Set,这两部分操作将会暂停程序中的应用程序,然后并发重置空闲的一些Region块,将它们放回至空闲列表中。最后的拷贝阶段也是基于stop-the-world机制的

拷贝阶段:该阶段的主要任务是将存活对象复制到未使用过的Region块


0 0
原创粉丝点击