GC算法和种类

1.GC的概念

  • Garbage Collection垃圾收集
  • Java中,GC对象时堆空间和永久区

2.Garbage Collection算法

①引用计数法

通过引用计算来回收垃圾

原理:对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,当引用失效时,引用计数器就减1,只要对象A的引用计数器的值为0,则对象A就不可能再被使用,可以进行回收了。

上述从根对象(GcRoot对象)出发,向下搜索,走过的路径称为引用链,如果根对象与某个对象之间存在引用链,那么该对象就是可达的,反之,不存在引用链,即不可达。当一个对象失去了从根对象出发的引用链时,即变为了不可达对象,就对该对象进行了垃圾回收。

引用计数法存在的问题

--引用和去引用伴随着加法和减法,影响性能

--很难处理循环引用

上述循环引用问题,即使从根对象出发,它们之间不可达,但是由于引用计数法原理,只要引用计数器的值不为零,那么该对象就不会被清理,但是实际上这三个对象都是应该被清理的对象,因为它们对根对象来说已经不可达了。

②标记清除法

原理:将垃圾回收分为两个阶段:标记阶段和清除阶段。一种可行的实现是,在标记阶段,首先通过根节点,标记从根节点出发的可达对象,因此,未被标记的对象就是未被引用的垃圾对象,然后,在清除阶段,清除所有未被标记的对象。

上图中,箭头表示引用,从根节点出发,有箭头的地方经过的节点都是可达对象,即浅灰色的部分,而深灰色的对象都是不可达对象。算法对可达对象进行标记,然后将未被标记的对象进行清理。

③标记压缩法

适用于存活对象较多的场合,如老年代。他在标记-清除的基础上做了一些优化,和标记-清除算法一样,标记-压缩算法也首先需要从根节点开始,对所有可达对象做一次标记,但是之后,它并不简单的清理未标记对象,而是将所有的存活对象压缩到内存的一端,之后,清除边界外所有的空间。

将存活对象,压缩到一边,然后将其之外的所有空间全部清理。

④复制算法

--与标记-清除算法相比,复制算法是一种相对高效的回收方法

--不适用于存活对象比较多的场合,如老年代

原理:将原有的内存空间分为两块,每次只使用其中块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收

将存活对象复制到未使用的那块内存空间中,将正在使用的含有垃圾对象的内存空间,完全清空,然后清理前未使用与已使用的两块空间角色互换

--复制算法最大问题:空间浪费

通过整合标记清理思想进行改进

老年代空间作为复制算法的担保空间,第一块最大的作为主要存储空间(对象产生的地方,新生代中的eden),中间两块(from和to)作为复制算法的核心;垃圾回收开始之后,首先大对象直接进入担保空间,大对象进入复制空间不合理的原因:①复制空间可能不会很大,因为越大资源浪费越严重,因此大对象尽可能不要在复制空间分配,否则会引起两个问题:①如果大对象放下复制空间,很多小对象可能没地方去,就会排挤到老年代中②大对象根本复制空间放不进去,就只能去老年代。因此大对象一般去担保空间。老年对象进入老年代,当年轻对象每被清理一次,对象年龄就会加1,如果经过几次清理都没有被清理掉,是一个长期有效的对象,就会变成老年代。剩余对象(小对象、年轻对象)进入复制空间,然后清空原先使用的空间。

了解GC之后,重新回顾堆空间的内存分析

由上面堆信息可知,

新生代的实际大小=(0x28d80000-0x27e80000)/1024/1024=15M

而实际可使用的是total值=13824k = eden + from或to中的其中一个=12288+1536;

因为采用的是复制算法,from和to之中总有一个要拿来当复制空间,即少掉1536k大小的复制空间,所以实际可用的空间就是15M-1536K=13824k.

⑤分代收集算法

原理:根据对象存活周期的不同将内存划分为几块,一般是把java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率较高、没有额外空间对他进行分配担保,就必须使用“标记清理”或者“标记压缩”算法进行回收。

⑥GC算法总结整理

--引用计数:没有被java采用,因为存在对象不会被清理的情况

--标记清除

--标记压缩  :两者普遍用于老年代的垃圾收集

--复制算法:用于新生代的垃圾收集

3.可触及性

①可触及:从根节点出发,可以触及到这个对象,即可达

②可复活:

                --一旦所有引用被释放,就是不可达状态,不可达并不是一定不可触及

                --因为在finalize()中可能复活该对象,但是同个对象finalize方法只会进行一次

③不可触及:

--在finalize()后,可能进入不可触及状态

--不可触及对象不可能复活

--可以回收

public class TestRelive{
	public static TestRelive tr;
	
	@Override
	protected void finalize() throws Throwable{
		super.finalize();
		System.out.println("TestRelive finalize called");
		obj = this;
	}
	
	@Override
	public String toString(){
		return "i am TestRelive object";
	}
	
	public static void main(String[] args) throws Exception{
		tr = new TestRelive();
		tr = null;//不可达,但不一定不可触及
		System.gc();
		Thread.sleep(1000);
		if(tr == null){
			System.out.println("tr is null");
		}
		else{
			System.out.println("tr is useable");
		}
		System.out.println("the second gc");
		tr = null;
		System.gc();
		Thread.sleep(1000);
		if(tr == null){
			System.out.println("tr is null");
		}
		else{
			System.out.println("tr is useable");
		}
	}
}

当第一次tr设置成null的时候,tr处于不可达状态,并且系统调用gc,由于重写了finalize方法,在gc之前需要调用finalize,使得tr又重新处于可达状态,第二次gc的时候,由于finalize只会执行一次,所以tr就真正处于不可触及的状态了,故被清理了。

注意:

--经验:避免使用finalize(),操作不慎可能导致错误

--finalize 优先级低,何时被调用,不确定

--何时发生gc不确定

--如果需要释放资源,可以使用try-catch-finally来替代它

根对象

-栈中引用的对象

-方法区中静态成员或者常量引用的对象,全局对象

-native方法栈中引用的对象

4.Stop the world

       --java中一种全局暂停的现象,全局停顿,所有java代码停止,native代码可以执行,但不能和JVM交互

       --多半由于GC引起

       ------Dump线程

       ------死锁检查

       ------堆dump

为什么GC时会产生全局停顿?

类比在聚会时打扫房间,聚会时很乱,又有新的垃圾产生,房间永远打扫不干净,只有让大家停止活动了,才能将房间打扫干净

危害

--长时间服务停止,没有响应

--遇到HA系统,可能引起主备切换,严重危害生产环境

平常由主机工作,备机不工作,一般情况下,不允许主备机同时工作,会出现系统问题。如果此时主机发生gc,系统长时间没有反应,备机以为主机出现问题,此时备机开始工作;等到主机gc结束,则主备机同时开始工作,严重危害生产环境。

 

 

图片资源来自-------------炼数成金,深入JVM内核

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值