Java垃圾回收机制(GC)

1 篇文章 0 订阅

Java垃圾回收机制是JVM提供的一种回收内存垃圾的能力,是自动执行的。

当程序中的对象一旦没有使用,JVM则认为该对象成为“垃圾”,GC会及时回收,减少内存不必要的浪费。


如何确定对象是否为“垃圾”?

1.引用计数算法

给对象添加一个引用计数器,每次对象被引用时,计数器加1,对象被取消引用时,计数器减1。当任何时刻计数器都为0的对象就认为不会再被引用了,GC会进行回收。

这种方法虽然简单,效率也很高,但是这种判断方法有个很明显的缺点,就是不能很好的解决循环引用的问题。所以Java并没有采用引用计数法来管理内存。

可以通过这个例子来说明:

class MyClass {
	public Object objcet = null;
}

public class JavaMain {
	
	public static void main(String[] args){
		MyClass object1 = new MyClass();
		MyClass object2 = new MyClass();
		
		object1.objcet = object2;
		object2.objcet = object1;
		
		object1 = null;
		object2 = null;
	}

}
上面的代码中,最后把object1和object2赋值为null,说明该两个对象不可能有引用,但是它们相互引用对方,计数器的值不可能为0 ,也就是说GC始终回收不了这两个对象。


2.根搜索法

GC Root Tracing 算法思路就是通过一系列的名为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连,即从GC Roots到这个对象不可达,则证明此对象是不可用的。Java采用该方法判断对象是否为无用转态。

上图,左边的对象都是存活的,右边的都是可以回收的。


常用垃圾收集算法:

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

此算法思路很简单,就是先标记无用的对象,然后通过标记统一回收无用的对象。

采用此算法回收后,内存块就不连续了,容易产生内存碎片,导致下一次分配大内存块的时候无法分配。



2.Copying(复制)算法

Copying算法将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。内存回收时,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。过程如下图所示:


这种方法实现简单,运行高效不易产生碎片,但是能用的内存大小缩减了一半,所以对内存要求很大,并且长期的复制拷贝对性能也会产生影响。


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

此算法的标记阶段和Mark-Sweep(标记-清除)算法一样,但是Mark-Compact算法并不是直接清除需要回收的对象,而是将存活的对象整理到一端,然后清理掉端边界以外的内存。过程图如下:



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

Java的垃圾回收机制采用的就是分带收集算法,分代收集算法主要将对象存活期的长短将内存进行划分。

Java heap(堆):

为什么要先说java 堆,因为java中内存的分配和回收都是在java堆中进行的,java堆是java程序中对象的活动空间,存储着程序所用到的所有对象。当对象没用引用的时候,Java回收机制(GC)负责回收这些无用的对象,减少内存开销。

Java堆可以分为两个部分:
(1)新生代(PSYoungGen):新生成的对象会存入该区域,经过一定次数的GC循环,如果没有被GC回收,则会被存入老年代。在新生代中,对象存活率低,存活周期较短,所以采用Copying(复制)算法来进行内存回收。

(2)老年代(ParOldGen):存储生存周期较长的对象,里面的对象是从新生代移入。老年代中的对象存活率较高,存活周期也相对较长,所以采用Mark-Compact(标记-整理)算法来进行内存回收。

还有一个是永久代(PSPermGen),一般指java的方法区间和常量池。永久代是区别于Java堆的独立区域。如下图:

新生代(PSYoungGen)又划分为三个区域:Eden Space,From space,To space。如图所示:

对于新生成的对象,主要会放到Eden Space和From Space中去,当Eden Space和From Spance存满过后,GC开始收集垃圾对象,并把存活的对象复制到To Space中,清理Eden Space和From Space里面的所有内存。清理过后,对象就存在了Eden Space 和To Space中,当To Space存满后,GC会将存活的对象复制到From Space中,清理To Space中的所有内存,如此反复循环。看到这里可能有人会问,为什么要一次一次的循环,什么时候循环结束?原因是:应用程序生成的绝大部分对象都是短命的,copying算法最理想的 状态是,所有移出Eden的对象都会被收集,因为这些都是短命鬼,经过一定次数的GC后应该被收集,那么移入到旧域的对象都是长命的,这样可以防止From Space 和 To Space来回复制影响应用程序。实际上这种理想状态是很难达到的,应用程序中不可避免地存在长命的对象,copying算法的发明者要这些对象都尽量放在新域中,以保证小范围的复制,压缩老年代的开销可比新生代中的复制大得多。JVM中循环次数默认的是15次,通过-XX:MaxTenuringThreshold=15 属性配置。当复制到15次后,JVM会把存活的对象移到老年代中。

老年代中采用的是Mark-Compact(标记-整理)算法,将存活的对象压缩到内存的一端,清空端边界的外的所有垃圾对象。

参考:

http://blog.csdn.net/initphp/article/details/30487407

http://blog.csdn.net/jiafu1115/article/details/7024323#comments

http://www.zhihu.com/question/35164211/answer/68265045


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值