初步探讨垃圾回收机制

笔记 同时被 3 个专栏收录
36 篇文章 0 订阅
22 篇文章 0 订阅
33 篇文章 0 订阅

写在前面

社会上难免会有各种各样的偏见,年龄可以被否认,学历可以被否认,能力可以被否认,经验也可以被否认,但否认不了的,是我们年轻人一往无前的干劲,当有一天你能正确的接受和看待各种各样的偏见,不与他人相争,不与他人相比。静下心来,做自己的领域,成为自己心里的专家,做自己的偶像。万物没有什么可以争议,万事也没有什么值得惊奇,走自己的路,让别人说去吧。

大话垃圾回收

我们来探讨一下垃圾回收这个话题,垃圾回收是性能调优的重中之重,绝对值得我们深入研究。
垃圾回收的内容水非常的深,细节也非常的多,小飞龙在准备这部分专题的时候,真的做了大量的攻克与精力。不过小伙伴们不要担心,这部分内容从难度上来讲,并不是那么难以接受。所以大家不要灰心。
我还是建议大家,对这部分的知识一定要有耐心,多花一些时间,多记笔记。本专题分为多篇文章来讲解的介绍,后续的内容会慢慢更新。

步入正题

我们知道,在开发java程序的时候,我们一般是不需要关注对象的回收,而是用java的垃圾回收机制帮助我们自动回收掉没用的对象。
但是 JVM提供了多种垃圾回收算法,以及多种垃圾回收策略,不同的回收算法与策略都有不同的适用场景,如果你在项目中使用了不合适的回收算法与策略,那么系统的性能就很难达到最优。
在某些场景下,不合适的垃圾回收算法或者策略,甚至导致系统的性能大幅度下降。所以说垃圾回收的重要性是毋庸置疑的。

我们主要探讨三个话题:

  • 什么场景下使用什么样的垃圾回收策略?
  • 垃圾回收发生在哪些区域?
  • 对象在什么时候能够被回收?

什么场景下使用什么样的垃圾回收策略?

在对内存要求苛刻的场景:想办法提高对象的回收效率,多回收掉一些对象,腾出更多的内存。
在CPU使用率高的情况下:降低高并发时垃圾回收的频率,让CPU更多的执行你的业务而不是垃圾回收。

垃圾回收会发生在哪些区域?

我们知道,在JVM内存结构中,虚拟机栈、本地方法栈以及程序计数器,都是线程独享的,这三个区域随着线程的创建而创建,随着线程的销毁而销毁。而栈里的栈帧又会随着方法的进入和退出分别进行入栈和出栈的操作。所以呢,这三块区域是不需要考虑垃圾回收的,而堆和方法区是线程共享的,这两块区域才需要考虑垃圾回收。 堆是垃圾回收的主要区域,主要用来回收我们创建的对象。方法区则需要回收废弃的常量,以及不需要使用的类。

对象在什么时候能被回收呢?

就目前来说,主要有两种算法判断对象在什么时候被回收。

  1. 引用计数法
    通过对象的引用计数器来判断该对象是否被引用(如果存在循环引用会出现问题)
  2. 可达性分析
    以根对象(GC Roots)作为起点向下搜索,走过的路径被称为引用链(Reference Chain),如果某个对象到根对象没有引用链相连时,就认为这个对象是不可达的,可以回收。
GC Roots包括哪些对象?
  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI(即Native方法)引用的对象
什么是引用?

从jdk1.2开始,Java里面涉及了四种引用:

  1. 强引用(Strong Reference)
    形如Object obj = new Object()的引用
    只要强引用在,永远不会回收被引用的对象

  2. 软引用(Soft Reference)
    比如SoftReference<String> sr = new SoftReference<>("hello")
    是用来描述一些有用但非必要的对象
    软引用关联的对象,只有在内存不足的时候才会回收

  3. 弱引用(Weak Reference)
    比如WeakReference<String> sr = new WeakReference<>("hello")
    弱引用也是用来描述非必要的对象
    无论内存是否充足,都会回收被弱引用关联的对象

  4. 虚引用
    比如
    ReferenceQueue\<String> queue = new ReferenceQueue<>();
    PhantomReference\<String> pr = new PhantomReference<>("hello",queue);

    不影响对象的生命周期,如果一个对象只有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动,必须和引用队列(ReferenceQueue)配合使用。当垃圾回收器准备回收一个对象时,如果发现他还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动

可达性分析注意点

对于可达性分析还有一个注意点,就是:
即使一个对象不可达,也不一定会被回收

对象不可达,只是给这个对象一个死缓,要想处死这个对象,大致的流程是这样的:
在做垃圾回收的时候,首先会判断对象有没有和根对象的引用链,如果存在引用链,那么就不回收,如果没有引用链,就会给他判死缓。
那么怎么给这个对象判死刑呢??
JVM首先会判断有没有必要去执行这个对象的finalize(),或虚拟机已经调用过finalize(),如果你的对象没有重写finalize()或者finalize()已经被调用过,都会认为没有必要再执行了。
如果没有必要执行finalize()那么直接被回收,
那么如果判断有必要执行finalize(),那么会将对象放入F-Queue,虚拟机自动创建低优先级线程finalizer执行对象的finalize(),如果你在finalize()重新与引用链任意一个对象建立链接,那么这个对象就会从F-Queue中移除,不去回收,因为你已经重新建立链接了,这个对象又变成可达的了。
那么如果调用过finalize()方法之后,这个对象还是没有到根对象的引用链,那么这个对象也会被回收。

finalize()使用建议

  • 避免项目中使用finalize()方法,操作不当可能导致问题
  • finalize()优先级低,何时会被调用无法确定,因为什么时间发生GC不确定。
  • 实际项目中,应尽可能使用try cache finally 来代替finalize方法
  • 2
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值