Java垃圾回收机制

Java垃圾回收机制

一、如何确定一个对象是否可以被回收?
1、引用计数算法:判断对象的引用数量
引用计数算法通过判断对象的引用数量决定对象是否可以被回收;引用计数算法的堆内每个对象实例都有一个引用计数,当一个对象被创建时,且该对象实例分配给一个引用变量,该对象实例的引用计数设置为1,当任何其他变量被赋值为这个对象的引用时,对象实例的引用计数就加1;当一个对象实例的某个引用超过了生命周期或被设置为一个新值时,该对象实例的计数就减1;当一个对象实例被垃圾收集时,它引用的任何实例对象的计数器均减1,任何计数器为0的对象实例可以被当作垃圾收集;
引用计数收集器可以很快执行,并且交织在程序运行中,对程序需要不被长时间打断的实时环境比较有利;
缺点:很难解决对象之间相互循环引用的问题。
2、可达性分析算法:判断对象的引用链是否可达来决定对象是否可以被回收
可达性分析算法通过一系列“GC Roots”的对象最为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象的GC Roots没有任何引用链相连时,则此对象时不可用的。
GC Roots对象包括:
(1)虚拟机栈(栈帧中的局部变量表)中引用的对象;
(2)方法区中的类静态属性引用的对象;
(3)方法区中常量引用的对象;
(4)本地方法栈中Native方法引用的对象;
二、垃圾收集算法
1、标记–清除算法:此算法分为标记和清除两个阶段,该算法首先从根集合进行扫描,对存活的对象标记,标记完毕后,在扫描整个空间中未被标记的对象并进行回收;
缺点:
(1)效率问题: 标记和清除两个过程的效率都不高;
(2)空间问题:该算法不需要进行对象的移动,并对不存活的对象进行处理,因此标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序进行过程中需要分配较大的对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作;
2、复制算法:将可用内存按容量划分为大小相等的两块,每次只使用其中一块,当一块内存用完时,就将还存活的对象复制到另外一块上面,然后再将已使过的内存空间一次清理掉,这种算法适用于对象存活率低的场景(如新生代),这会使得每次都是对整个半区进行回收,内存分配时不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,进行高效;
注:新生代中的对象每次回收都基本只有10%左右的对象存活,所以需要复制的对象少
3、标记–整理算法:复制算法在对象存活率较高时要进行较多的复制操作,效率低,所以老年代一般使用标记–整理算法,该算法的标记过程与标记–清除算法类似,但后半步骤不是直接堆可回收对象进行清理,而是让所有存活对象都向一段移动,然后直接清理掉端边界以外的内存,适用于存活率较高的年代。
4、分代收集算法:当创建的对象和方法变量较多时,堆内存中的对象也变多,不同对象生命周期不同,位于堆中的区域也不同,因此,对堆内存不同区域采用不同的策略进行回收可以提高JVM执行效率,即新生代存活率低采用复制算法,老年代存活率高采用标记–清除算法或标记–整理算。
(1)新生代:目标是尽可能快速的收集掉那些生命周期短的对象,一般,所有新生成的对象首先是放在新生代的,新生代按8:1:1的比例分成Eden区和两个survivor(s0,s1)区,大部分对象在Eden区生成,垃圾回收时,先将eden区存活对象复制到s0区,然后清空Eden区,当s0区满时,则将eden区和s0区存活对象复制到s1区,然后清空eden区和s0区,此时s0区空时,交换s0和s1区角色(下次垃圾回收时会扫描eden区和s1区),即保持s0区为空。当s1区也放不下eden区和s0区存活的对象时,将存活对象爱国直接放到老年代,若老年代也满了,会触发一次FullGC,即对新生代,老年代都进行回收,新生代的回收GC称为Minor GC(发生频率高),不一定等Eden区满才触发。
(2)老年代:存放生命周期较长的对象,即新生代中经历了N次垃圾回收后仍然存活的对象会被放到老年代中,老年代的内存也比新生代大很多,当老年代满时会触发Major GC,老年代中存放的对象存活时间长,因此,Full GC发生的频率较低。
(3)永久代:主要存放静态文件,如Java类,方法等,永久代对垃圾回收没有显著影响;

垃圾回收器

垃圾回收器是内存回收的具体实现
用于回收新生代的收集器:Serial 、ParNew、Parallel Scavenge
用于回收老年代的收集器:Serlal Old、Parallel Old、CMS
用于回收整个Java堆的G1收集器:G1
Serial收集器(复制算法):新生代单线程收集器,标记和清理都是单线程;
优点:简单高效;
ParNew收集器(复制算法):新生代并行收集器, 在多核CPU环境下比Serial收集器更好;
Parallel Scavenge收集器(复制算法):新生代并行收集器,追求高吞吐量,高效利用cpu,吞吐量高可以高效利用cpu时间,尽快完成程序的运算任务,适合后台应用等对交互相应不高的场景;
Serlal Old收集器(标记-整理算法):老年代单线程收集器;
Parallel Old收集器(标记-整理算法):老年代并行收集器,吞吐量优先;
CMS收集器(标记-清除算法):老年代并行收集器,追求最短回收停顿时间为目标的收集器,具有高并发、低停顿的特点,追求最短GC回收停顿时间;
内存分配与回收策略:自动内存管理解决的问题,给对象分配内存以及回收分配给对象的内存。一般,对象主要分配在新生代的Eden区上,若启动了本地线程分配缓存(TLAB),则按线程优先在TLAB上分配,少数情况直接分配在老年代,内存分配取决于当前使用的是哪一种垃圾回收器组合,还有虚拟机中于内存相关参数的设置;
(1)对象优先在Eden分配,当Eden区没有足够空间进行分配时,虚拟机将发起一次“MinorGC”;
(2)大的对象直接静茹老年代;
(3)长期存活的对象将进入老年代;
(4)动态对象年龄判定:虚拟机并不要求对象年龄必须达到MaxTenuringThreshold才能进入老年代,若在survivor空间中相同年龄的对象大小总和大于survivor空间的一半,年龄大于或等于该年龄对象可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。
垃圾收集器回收:无任何引用对象占据的内存空间而不是对象本身。
强引用:普遍存在的比如“Object obj = new Object()”这类引用,只要强引用还在,垃圾回收器就永远不会回收被引用的对象;
软引用:描述一些还有用,但并非必需的对象,对于软引用关联的对象,在系统将要发生内存溢出异常之前,会将这些对象列入回收范围之内并进行第二次回收,若这次回收之后还是没有足够的内存,才会抛出内存溢出异常,JDK1.2之后,出现SoftReference类实现软引用;
弱引用:描述一些非必需的对象,但强度比软引用更弱一些,被软引用关联的对象只能生存到下一次垃圾回收集发生之前,当垃圾收集器工作时,无论当前内存是否足够,都会回收掉被弱引用关联的对象,JDK1.2之后,出现WeakReference类实现弱引用;
虚引用:最弱的引用关系,一个对象是否有虚引用的存在,完全不会 对其生命时间构成影响,也无法通过虚引用采取一个对象实例,目的是希望能在对象被回收器回收之前收到一个系统通知,JDK1.2之后,出现PhantomReference类实现虚引用;

判断一常量是否是“废弃常量”:常量池中某常量没有任何一个对象引用,也没有其他地方引用这个字面量,发生内存回收,该常量则被回收;
判断一个类是否是“无用类”
(1)该类所有的实例都以及被回收,即Java堆中不存在该类的任何实例;
(2)加载该类的ClassLoader已经被回收;
(3)该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法。

以上内容整理自:http://blog.csdn.net/justloveyou_/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值