JVM第二天 -(二)垃圾回收

1. 如何判断对象可以回收
2. 垃圾回收算法
3. 分代垃圾回收
4. 垃圾回收器
5. 垃圾回收调优

1. 如何判断对象可以回收

1.1 引用计数法

        对象有一个记录引用个数的计数,只要其他变量引用该对象,计数就加1。当变量不再引用它,计数就减1。计数为0,表明该对象不再被引用,可作为垃圾回收。

        但是该计数法有一个弊端,就是产生循环引用,如下图中,AB对象各自引用对方,引用计数都不为0,即使没人再引用他们俩,但是还是不能被回收,造成内存泄露。

 1.2 可达性分析算法

        可达性分析算法首先要确定一系列根对象。

         ● Java 虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象
         ● 扫描堆中的对象,看是否能够沿着 GC Root 对象 为起点的引用链找到该对象,找不到,表示可以回收

        首先确定一系列根对象,所谓根对象,可以理解为那些肯定不能被垃圾回收的对象。在垃圾回收之前,首先会对堆中的所有对象进行一次扫描,查看某个对象是不是直接被根对象直接或间接引用,如果是,则不能回收,不是,则可以回收
         ● 哪些对象可以作为 GC Root ?

      1.2.1 通过MAT工具查看堆内存中可作为GC root的对象有哪些

        演示代码

//演示GC root
    public static void main(String[] args) throws InterruptedException, IOException {
        List<Object> list1 = new ArrayList<>();
        list1.add("a");
        list1.add("b");
        System.out.println(1);
        System.in.read();

        list1 = null;
        System.out.println(2);
        System.in.read();
        System.out.println("end...");
    }

        

注意:

        注意区分引用变量和对象,如list1只是一个引用,他是存储在活动栈帧里的,他是一个局部变量,他后面引用的对象是存储在堆里的,根对象也是指堆中的对象,而不是引用变量

        先把堆内存的当前状态转储成一个文件,jmap -dump:format=b,live,file=1.bin $pid(b是抓取成二进制文件,live是指只抓取存活对象,被回收的对象不抓取,live在抓取之前会主动触发一次垃圾回收,file表示存的哪个文件)

1、list1置空之前:

(1)System Class  ,系统类,由启动类加载器加载的类,都是核心的类,一定不会被回收。

(2)Native Stack,包含操作系统引用的一些Java对象。Java虚拟机在进行方法调用时,必须执行一些操作系统的方法,而操作系统需要引用一些Java对象

(3)Busy Monitor,已被加锁的对象,也可作为根对象

(4)Thread,活动线程,其对象不可被回收,线程运行时都由一次次的方法调用组成,每次方法调用都会产生一个栈帧,栈帧内使用的东西都可以作为根对象。

 如活动线程执行过程中局部变量引用的对象都可以作为根对象,如ArrayList,方法参数args引用的字符串数组对象也是根对象。

 2、list1置空之后:主线程内已无ArrayList,局部变量置为null,表示不再引用ArrayList对象。然后执行jmap -dump:format=b,live,file=2.bin 2024,触发GC,被GC垃圾回收掉,所以在根对象列表中无法找到他。

1.3 四种引用

        

 1. 强引用
        ● 只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收

        实际上平时用的所有的引用都是强引用,如通过=运算符把新建的对象赋值给一个变量,称这个变量强引用了这个对象。强引用的特点是:只要沿着GC Root的引用链能找到他,就不会被垃圾回收。B对象和C对象都强引用了A1对象,当GC Root对他的强引用都断开时,才可以被垃圾回收。称之为强引用。


2. 软引用(SoftReference)
        ● 仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次触发垃圾回收,回收软引用
对象

        ● 可以配合引用队列来释放软引用自身

        只要对象没有被直接的强引用所引用,在垃圾回收时,都可以被回收掉。如A2对象有一个软引用引用他,被B对象直接强引用,如果B对象不再引用他,则可以在垃圾回收发生时,被垃圾回收掉。

       
3. 弱引用(WeakReference)
        ● 仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象
        ● 可以配合引用队列来释放弱引用自身

        软引用是相同前提条件下内存不足时才会再次触发垃圾回收,回收掉软引用对象。

        软、弱引用还可配合回收队列工作,当软引用的对象被回收掉(这里例如A2对象被回收掉),软引用本身是一个对象,他会进入引用队列,弱引用对象也是如此。他们自身也要占用内存,如果想要释放他俩,则需要使用引用队列找到他俩,依次遍历释放。


4. 虚引用(PhantomReference)
        ● 必须配合引用队列使用,主要配合 ByteBuffer 使用,被引用对象(这里指ByteBuffer)回收时,会将虚引用入队,由 Reference Handler 线程调用虚引用相关方法(Unsafe.freeMemory())释放ByteBuffer申请的直接内存

 

 


5. 终结器引用(FinalReference)
        ● 无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象
暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 finalize
方法,第二次 GC 时才能回收被引用对象

 

 1.4 软引用应用

        软引用通常使用在什么场景,来看这样一个案例。

       案例代码

package cn.itcast.jvm.t2;

import java.io.IOException;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;

/**
 * 演示软引用    后两个参数表示打印垃圾回收的详细参数
 * -Xmx20m -XX:+PrintGCDetails -verbose:gc
 */
public class Demo2_3 {

    private static final int _4MB = 4 * 1024 * 1024;



    public static void main(String[] args) throws IOException {
        /*List<byte[]> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            list.add(new byte[_4MB]);
        }

        System.in.read();*/
        soft();


    }

    public static void soft() {
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值