一:如何确定对象是否为垃圾对象
1.主要原则就是看对象是否可达,而对象是否可达就是看对象是否被引用,java对象都有一个引用计数属性,每被引用一次,就+1,每被释放一次,就-1.(还有一种方法是通过可达性分析,堆内存实际可以看成是一个有向图)
2.对象引用类型
(1)强引用:即使内存不够,溢出了,jvm也不会回收此类对象,当强引用对象之间的关联被切断时,才有可能被回收(如果方法内部进行引用,当方法执行完毕,就会退出方法栈,强引用一样会失效)
Object obj = new Object();//强引用 obj = null;//切断强引用与对象的联系,表示JVM可以回收Object对象
(2)软引用:只有jvm内存不够用时,才会回收此类对象
SoftRererence sr = new SoftReference<Object>(new Object());//软引用 Object obj = sr.get()//获取软引用指向的对象
一般使用场景为缓存,当内存不够用时才清理
(3)弱引用:jvm即使内存不够用,一旦进行垃圾回收,弱引用指向的对象也会被回收
WeakReference wr = new WeakReference<Object>(new Object())//弱引用 Object obj = wr.get()//获取弱引用的对象
使用场景一般是缓存
(4)虚引用: 与若引用的回收机制类似,但是无法通过虚引用来获取一个对象的真实引用,即无法通过虚引用获取对象实例 ,需要结合引用对象来使用
ReferenceQueue queue = new ReferenceQueue();//引用队列 PhantomReference<Objecr> pr = new PhantomReference<Object>(new Object(),queue);//虚引用 pr.get()//返回空值,因为无法通过虚引用获得真实引用
使用场景:监控垃圾对象的回收。当一个对象将要被回收时,回收器若发现其还有一个虚引用的话,会把该虚引用加入到引用队列中,然后再回收该对象。比如一个对象既有强引用,又有虚引用,使用某种方法把强引用与对象的关联切断,那么回收器就会回收该对象,但是回收器发现此对象还有虚引用,就会把该虚引用放进队列中,所以监控队列就可以看到有什么对象被回收了
二:GC回收算法
1.标记-清理算法:利用可达算法(对象是否被回收),标记不可达的对象,然后清理这些不可达对象
缺点:不可达对象很可能内存是不连续的,这里对象被清理后空出来的内存空间很可能大小满足不了新的对象,最终这些内存很可能被丢失。也就是导致内存碎片的丢失,这种算法适合可达对象多的场景
2.标记-复制算法
把内存空间分成两份,一份用来存对象,一份先空着。利用可达算法,标记出不可达对象,然后把可达对象复制到另一份空的内存空间,原内存空间的对象全部删除。
缺点:空间利用不完全,浪费一般的内存空间,适合不可达对象多的场景(减少复制量)
3.标记压缩算法
与标记-清理算法类似,清理完不可达对象后,会整理内存碎片,防止内存泄漏,解决了标记-清理算法的问题
缺点:内存碎片整理开销大,适合可达对象多的场景
4.分代算法
分代算法结合了标记-清理算法与标记压缩算法,把堆分成了新生代与老年代,新生代又分为eden区,from区以及to区,所有刚刚创建出来的对象都放在eden区,当eden区满了之后,使用复制算法把可达对象从from区或者to区任意一个没有使用的内存空间,假设是to区(from区中存活的对象也会被放进to区),然后再把eden区和from区的对象都直接清理掉。另外:新生代不可达对象多,因此使用了标记-复制算法,老年大可达对象多,因此使用了标记压缩算法
新生区对象转移到老年区的条件
(1)对象太大(虚拟机参数可以调这个阈值)
(2)转移过程,如上述的to区满了,多出来的对象也是直接转移到老年区
(3)复制15次后,依然还存活的对象(对象存储该年龄的空间大小为4个bit,最大十进制是15)
最后:附上后端技术交流圈,欢迎各位大佬入圈交流技术......V:ff1341658(先添加好友后拉群,请备注:小白不黑)