java垃圾是怎么回收的,回收算法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java垃圾是怎么回收的,回收算法相关的知识,希望对你有一定的参考价值。

Java ,C#语言与C/C++语言一个很大的区别是java与C#具有自动垃圾回收机制。C++程序员经常需要绞尽脑汁的分析哪里出现了内存泄漏。而在java,C#中,虽然有时也会出现内存泄漏,但大部分情况下程序员不需要考虑对象或者数据何时需要被销毁。因此程序员不会因为错误的释放内存而导致程序崩溃。垃圾回收的缺点是加大了程序的负担,有可能影响程序的性能。
1.垃圾收集器的主要功能有
(1) 定期发现那些对象不再被引用,并把这些对象占据的堆空间释放出来。
(2) 类似于操作系统的内存管理,垃圾收集器还需要处理由于对象动态生成与销毁产生的堆碎块,以便更有效的利用虚拟机内存。
2.区分活动对象与垃圾的算法
(1)引用计数法
堆中每一个对象都有一个引用计数。当新创建一个对象,或者有变量被赋值为这个对象的引用,则这个对象的引用计数加1;当一个对象的引用超过生存期或者被设置一个新的值时,这个对象的引用计数减1。当对象的引用计数变为0时,就可以被当作垃圾收集。
这种方法的好处是垃圾收集较快,适用于实时环境。缺点是这种方法无法监测出循环引用。例如对象A引用对象B,对象B也引用对象A,则这两个对象可能无法被垃圾收集器收集。因此这种方法是垃圾收集的早期策略,现在很少使用。
(2)跟踪法
这种方法把每个对象看作图中一个节点,对象之间的引用关系为图中各节点的邻接关系。垃圾收集器从一个或数个根结点遍历对象图,如果有些对象节点永远无法到达,则这个对象可以被当作垃圾回收。
容易发现,这种方法可以检测出循环引用,避免了引用计数法的缺点,较为常用。
3.常用垃圾回收机制
(1)标记-清除收集器
这种收集器首先遍历对象图并标记可到达的对象,然后扫描堆栈以寻找未标记对象并释放它们的内存。这种收集器一般使用单线程工作并停止其他操作。
(2)标记-压缩收集器
有时也叫标记-清除-压缩收集器,与标记-清除收集器有相同的标记阶段。在第二阶段,则把标记对象复制到堆栈的新域中以便压缩堆栈。这种收集器也停止其他操作。
(3)复制收集器
这种收集器将堆栈分为两个域,常称为半空间。每次仅使用一半的空间,虚拟机生成的新对象则放在另一半空间中。垃圾回收器运行时,它把可到达对象复制到另一半空间,没有被复制的的对象都是不可达对象,可以被回收。这种方法适用于短生存期的对象,持续复制长生存期的对象由于多次拷贝,导致效率降低。缺点是只有一半的虚拟机空间得到使用。
(4)增量收集器
增量收集器把堆栈分为多个域,每次仅从一个域收集垃圾。这会造成较小的应用程序中断。
(5)分代收集器
这种收集器把堆栈分为两个或多个域,用以存放不同寿命的对象。虚拟机生成的新对象一般放在其中的某个域中。过一段时间,继续存在的对象将获得使用期并转入更长寿命的域中。分代收集器对不同的域使用不同的算法以优化性能。这样可以减少复制对象的时间。
(6)并发收集器
并发收集器与应用程序同时运行。这些收集器在某点上(比如压缩时)一般都不得不停止其他操作以完成特定的任务,但是因为其他应用程序可进行其他的后台操作,所以中断其他处理的实际时间大大降低。
(7)并行收集器
并行收集器使用某种传统的算法并使用多线程并行的执行它们的工作。在多CPU机器上使用多线程技术可以显著的提高java应用程序的可扩展性。
(8)自适应收集器
根据程序运行状况以及堆的使用状况,自动选一种合适的垃圾回收算法。这样可以不局限与一种垃圾回收算法。
4. 火车算法
垃圾收集算法一个很大的缺点就是难以控制垃圾回收所占用的CPU时间,以及何时需要进行垃圾回收。火车算法是分代收集器所用的算法,目的是在成熟对象空间中提供限定时间的渐进收集。目前应用于SUN公司的Hotspot虚拟机上。
在火车算法中,内存被分为块,多个块组成一个集合。为了形象化,一节车厢代表一个块,一列火车代表一个集合,见图一
注意每个车厢大小相等,但每个火车包含的车厢数不一定相等。垃圾收集以车厢为单位,收集顺序依次为1.1,1.2,1.3,1.4,2.1,2.2,2.3,3.1,3.2,3.3。这个顺序也是块被创建的先后顺序。
垃圾收集器先从块1.1开始扫描直到1.4,如果火车1四个块中的所有对象没有被火车2和火车3的对象引用,而只有火车1内部的对象相互引用,则整个火车1都是垃圾,可以被回收。
如图二,车厢1.1中有对象A和对象B,1.3中有对象C,1.4中有对象D,车厢2.2中有对象E,车厢3.3中有对象F。在火车1中,对象C引用对象A,对象B引用对象D,可见,火车2和火车3没有引用火车1的对象,则整个火车1都是垃圾。
如果火车1中有对象被其它火车引用,见图三,扫描车厢1.1时发现对象A被火车2中的E引用,则将对象A从车厢1.1转移到车厢2.2,然后扫描A引用的对象D,把D也转移到车厢2.2,然后扫描D,看D是否引用其它对象,如果引用了其它对象则也要转移,依次类推。扫描完火车1的所有对象后,剩下的没有转移的对象都是垃圾,可以把整个火车1都作为垃圾回收。注意如果在转移时,如果车厢2.2空间满了,则要在火车2末尾开辟新的车厢2.4,将新转移的对象都放到2.4,即火车的尾部)
补充说明:垃圾回收器一次只扫描一个车厢。图三中的对象B与C并不是立即被回收,而是先会被转移到火车1的尾部车厢。即扫描完1.1后,B被转移到火车1尾部,扫描完1.3后,C被转移到车尾。等垃圾收集器扫描到火车1尾部时,如果仍然没有外部对象引用它们,则B和C会被收集。
火车算法最大的好处是它可以保证大的循环结构可以被完全收集,因为成为垃圾的循环结构中的对象,无论多大,都会被移入同一列火车,最终一起被收集。还有一个好处是这种算法在大多数情况下可以保证一次垃圾收集所耗时间在一定限度之内,因为一次垃圾回收只收集一个车厢,而车厢的大小是有限度的。
参考技术A 1.Serial New/Serial Old
Serial/Serial Old收集器是最基本最古老的收集器,它是一个单线程收集器,并且在它进行垃圾收集时,必须暂停所有用户线程。Serial New收集器是针对新生代的收集器,采用的是Copying算法,Serial Old收集器是针对老年代的收集器,采用的是Mark-Compact算法。它的优点是实现简单高效,但是缺点是会给用户带来停顿。
2.Parallel New
Parallel New收集器是Serial收集器的多线程版本(参照Serial New),使用多个线程进行垃圾收集。
3.Parallel Scavenge
Parallel Scavenge收集器是一个新生代的多线程收集器(并行收集器),它在回收期间不需要暂停其他用户线程,其采用的是Copying算法,该收集器与前两个收集器有所不同,它主要是为了达到一个可控的吞吐量。
4.Parallel Old
Parallel Old是Parallel Scavenge收集器的老年代版本(并行收集器),使用多线程和Mark-Compact算法。
5.CMS
CMS(Current Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,它是一种并发收集器,采用的是Mark-Sweep算法。
6.G1
G1收集器是当今收集器技术发展最前沿的成果,它是一款面向服务端应用的收集器,它能充分利用多CPU、多核环境。因此它是一款并行与并发收集器,并且它能建立可预测的停顿时间模型。
参考技术B 算法。。。。。。。。。。。。。。。

java 垃圾回收算法和垃圾收集器

java垃圾回收算法以及原理

在java中常见的垃圾回收算法有四种,分别是标记清除算法、复制算法、标记整理算法以及分代回收算法。

标记清除算法(Mark-Sweep算法)

标记清除算法是最基础的垃圾回收算法,容易实现,而且思想也很简单。根据算法的名字我们就大致可以猜到这个算法分为两个阶段进行,一个是“标记”阶段,另一个就是“清除”阶段,“标记”阶段的任务就是要找出所有需要被回收的对象,清除阶段就是回收被标记的对象。 具体过程如图所示,

 

 

标记清除算法其实特别好理解,一阶段标记,二阶段清除被标记的对象,释放它们占用的内存资源。 优点:实现起来容易,效率比较高。 缺点:标记清除之后容易产生大量不连续的内存碎片,不连续的内存碎片浪费了,空间这可能会导致无法对大对象分配足够空间而提前触发新的一轮垃圾回收。

复制算法(Copying算法)

 

优点:实现简单,运行效率高,不容易产生内存碎片 缺点:为了解决产生内存碎片的问题,在内存使用方面付出了比较高的代价,使用这种方法, 会使原来的内存直接缩小了一半。如果是在存活对象过多的情况下,该方法的效率将会大大降低。

标记整理算法(Mark-Compact算法)

标记整理算法能够解决复制算法的缺陷,该算法在标记阶段和之前提到的标记清理算法一样,都是把垃圾对象标记出来,但是不同的是,在完成标记之后,该方法不是直接把对象清理掉,而是将存活的非标记对象都向同一方向移动,然后清理掉存活对象边界之外的无用内存。

分代收集算法(Generational Collection算法)

分代收集算法是目前大部分jvm的垃圾收集器采用的算法。它相比之前提到的三个算法更为复杂,该算法的核心思想就是分区,根据对象存活的生命周期划分为若干个不同的区域。一般情况下堆区会被分为老年代和新生代,老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收的时候会有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。 目前来说,大多数垃圾收集器对于新生代都采取复制算法,因为新生代中每次垃圾回收大部分对象,需要复制的存活对象数量较少,但是在新生代中并不是按照1:1来划分空间的,一般是先将新生代分为一块较大的块eden空间和两块较小的survivor空间,每次使用eden空间和其中一块survivor空间,进行内存回收的时候,将eden和survivor中还存活的对象复制到另一块survivor空间中,然后清理掉eden和刚才使用过的survivor空间。 对于每次回收对象数量较少的老年代来说,一般使用标记整理算法

jvm中典型的垃圾收集器有哪些

1.Serial/Serial Old收集器

年代最久远的收集器,它是单线程的,必须保证暂停了所有用户进程,才能进行垃圾收集。 Serial收集器是针对于新生代的收集器,采用复制算法,Serial Old收集器是针对老年代的收集器,采用的是标记整理算法。该收集器实现起来比较简单,但是缺陷是必须暂停其它所有用户进程。

2.ParNew收集器

Serial收集器的多线程版本,使用多个线程进行垃圾收集。

3.Parallel Scavenge 

收集器 新生代的多线程收集器,采用复制算法,和上述两种收集相比优点是不需要暂停其它用户进程。

4.Parallel Old收集器

是Parallel Scavenge收集器的老年代版本,因为针对老年代,所以使用标记整理法和多线程来处理垃圾对象。

5.CMS(Current Mark Sweep)收集器

一种并发的收集器,采用标记清除算法,是一种以获取最短回收停顿时间为目标的收集器 6.G1收集器

一款面向服务端应用的支持并发的收集器,能够充分利用多CPU、多核环境,并且能够建立可以预测的停顿时间模型。

垃圾回收相关参数

 

 

以上是关于java垃圾是怎么回收的,回收算法的主要内容,如果未能解决你的问题,请参考以下文章

带你读懂垃圾回收算法

带你读懂垃圾回收算法

java 垃圾回收算法和垃圾收集器

JVM专题--垃圾回收算法, 垃圾回收器

Java中的垃圾回收算法

怎么验证java的gc是不是会回收了某个不用的对象了。我有段代码有性能问题,需要调优,想知道怎么看