java 垃圾回收总结

1 如何判断哪些内存是可回收的

1) 最早是通过引用计数法(对象被引用,计数器加1,减少被应用减1),但是无法解决两个对象相互引用的问题

2)根节点查找法,对于一个对象,从GcRoot开始查找,如果没有一条路径能够到达该对象,则认为该对象认为是可被回收的,

但是不一定会被立即回收

 

2 如何回收?

通常垃圾回收采用分代收集法,将堆内存划分为新生代,老年代,而 方法区被认为是永久代,每个年代采用不同的回收算法

1) 新生代回收

  由于新生代的对象绝大部分都是生命周期很短的对象,所以采用复制算法进行回收,具体如下:

  将堆内存划分为一块Eden,和两块Survivor 区,比例为8(Eden):1(survivor):1(survivor),回收时,将Eden区和第一块Survivor区的

存活对象复制到第二块Survivor 中,然后清除掉Eden区和第一块Survivor区的需要回收的对象,

2)老年代

老年代中的对象绝大部分生命周期很长,即可回收的对象很少,所以可采用标记-清除(先标记可回收的对象,清除掉可回收对象,回收后的空间不连续)或标记-整理(先标记可回收的对象,将不需要回收的对象向内存的一端移动,然后清除掉可回收对象,保证回收后的空间连续)

虚拟机采用分代收集算法,那么就会对对象进行年龄标注,具体过程如下:

小Gc(MinorGc): 对新生代进行GC

 

大Gc(MajorGc): 对老年代进行GC

如果对象在Eden中经历过一次小GC(Minor Gc)后仍然存在,则将该对象复制到Survivor区,表标记年龄为1, 对象在Survivor 中没经历一次小GC(Minor Gc)后仍然存活,则年龄加1,当年龄增加到一定程度(默认为15)岁,则将其复制到老年代。

动态对象的年龄判断:

如果在Survivor区中,相同年龄的对象所在的内存超过Survivor空间的一般,则将大于这些对象的年龄的对象转移到永久代中

 

虽然JDK9之后用Cleaner机制代替了finalize机制进行内存清理,但是平时开发用的这里还是还是JDK8,emmmm.......就简单总结一下finalize机制吧。

finalize()是Object类下的方法,所以任何类都可以重写finalize()方法,JDK1就有finalize机制,不过一直饱受诟病。主要是因为如果一个类重写了finalize()方法,那么这个类的创建和销毁都变得额外麻烦。new这个类的实例时候,会额外new一个finalize实例指向这个类的引用,这也是一种开销,最关键的是回收这个对象时候要经过两次GC,这个类的实例可回收时候GC第一次准备回收发现还有一个finalize实例引用着这个类,就不能回收这个类的对象了,于是把这个类的引用放入一个finalize机制的引用队列java.lang.ref.Finalizer.ReferenceQueue,finalize有一个守护线程java.lang.ref.Finalizer$FinalizerThread不断的清理这个引用队列中引用,清理后再调用其finalize()方法,调用完后等待下一次GC去回收这个类的对象。

问题来了,finalize机制之所以容易造成内存泄漏,是因为守护线程java.lang.ref.Finalizer$FinalizerThread的优先级比用户线程低,这种情况下用户线程可能创建new实现finalize()方法的类实例的速度比该守护线程清理引用队列中引用速度快,这就导致大量的对象其实已经无用了可回收了但是积攒着等finalize()方法被调用后才能被GC回收,那这些对象不可用又占着内存,就是典型的内存泄漏了,越来越多之后就造成了OOM。

当然,在调用finalize()方法的时候这个类可以实现自我救赎即重新建立强引用避免被回收,例如把当前对象的引用this赋值给某对象的类变量/成员变量,重新建立可达的引用。这也很危险,除非是为了实现特定逻辑,不然对象很难被回收了很容易造成OOM。

那么调用finalize()方法有什么用呢?方法里面是不是有什么逻辑?对于对内存中的对象,其实finalize()方法并没有什么用,书上说:“它不是C/C++中的析构函数,而是Java刚诞生时为了使C/C++程序员更容易接受它所做出的一个妥协”。但是对于堆外内存,GC不管事的地方,finalize()方法就可以用来做资源回收随后的屏障了。
 

 

什么情况下触发垃圾回收

由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有两种类型:Scavenge GC和Full GC。

Scavenge GC

一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,
清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对
年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很
大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲
出来。

Full GC

对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个对进行回收,所以比Scavenge
GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调
节。有如下原因可能导致Full GC:
· 年老代(Tenured)被写满
· 持久代(Perm)被写满
· System.gc()被显示调用

 

详解链接:https://www.cnblogs.com/sunniest/p/4575144.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值