判断对象是否存活的算法

需要回收的位置如下

 其实垃圾回收是jvm自带的功能,所以有了如下的优缺点

优点:

1.项目开发的时候不需要开发人员考虑内存管理

2.可以有效的防止内存泄漏,更加高效的利用可使用的内存

3.因为垃圾回收不再有了作用于的概念

缺点:

因为不了解所以使用过程中会出现内存溢出和内存泄漏的问题

下面将判断对象存活的算法进行简单说明

引用计数算法:它的意思就是给每个创建的对象添加一个引用计数器,被引用计数值加1,引用失效时减一,当计数值为0时,表示该对象就不再被使用了

优点:

实现简单,执行效率高

缺点:

 检测不到循环引用

实际案例

 因为引用一直存在了,永远无法进行回收了,请注意java中的垃圾回收并没有使用引用计算算法

可达性分析算法:官方解释是通过一系列GC ROOTs 对象作为起始点,从起点开始向下搜索对象的路径,搜索所经过的路径称为引用链,当一个对象和任何一个GC ROOTs都没有引用链时,最终判断该对象不可用

先看图

 

再看下可以作为GC Roots的对象有哪些

1.栈帧中的局部变量表中的reference引用所引用的对象
2.方法区中static静态引用的对象
3.方法区中final常量引用的对象
4.本地方法栈中JNI(Native方法)引用的对象
5.Java虚拟机内部的引用, 如基本数据类型对应的Class对象, 一些常驻的异常对象(比如 NullPointExcepiton
6.OutOfMemoryError) 等, 还有系统类加载器。
7.所有被同步锁(synchronized关键字) 持有的对象

8.反映Java虚拟机内部情况的JMXBeanJVMTI中注册的回调、 本地代码缓存等

 这里可达性分析算法是java判断对象存活使用的

 但是这个时候的对象还并不能进入死亡,还需要再次的判断

下面通过模拟的程序进行说明

 

 代码部分

package com.java.test;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.ref.PhantomReference;
import java.lang.ref.WeakReference;

/**
 * @Description:
 * @Author: qiuxie
 * @Create: 2023/6/2 14:18
 */
public class FinalizeEscapeGc {
    /**
     * 定义日志对象
     */
    private static final Logger logger= LoggerFactory.getLogger(FinalizeEscapeGc.class);

    public static FinalizeEscapeGc SAVE_HOOK=null;

    public void isAlive(){
        logger.info("是的,我仍然活着");
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        logger.info("finalize 方法执行中");
        FinalizeEscapeGc.SAVE_HOOK=this;
    }

    public static void main(String[] args) {
        SAVE_HOOK=new FinalizeEscapeGc();
        logger.info("打印SAVE_HOOK:{}",SAVE_HOOK);
        //开始第一次的自救
        SAVE_HOOK=null;
        System.gc();
        //因为finalize()优先级比较低,设置等待0.5秒
        try {
            Thread.sleep(500);
            if (SAVE_HOOK!=null){
                logger.info("开始第一次判断是否存活");
                SAVE_HOOK.isAlive();
            }else {
                logger.info("第一次的自救 很抱歉,我已经死亡了");
            }
        } catch (InterruptedException e) {
            logger.error("睡眠异常:{}",e);
        }
        logger.info("第二次自救前 打印SAVE_HOOK:{}",SAVE_HOOK);
        //只能救一次,第二次自救会失败
        //开始第二次的自救
        SAVE_HOOK=null;
        System.gc();
        //因为finalize()优先级比较低,设置等待0.5秒
        try {
            Thread.sleep(500);
            if (SAVE_HOOK!=null){
                logger.info("开始第二次判断是否存活");
                SAVE_HOOK.isAlive();
            }else {
                logger.info("第二次的自救 很抱歉,我已经死亡了");
            }
        } catch (InterruptedException e) {
            logger.error("睡眠异常:{}",e);
        }

        logger.info("最后打印SAVE_HOOK:{}",SAVE_HOOK);

    }
}

  pom依赖

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

  这个org.slf4j已经被包含在springboot了

下面给出两次标记的走向图

 注意点:finalize()执行缓慢,会有发生死循环的可能性,甚至最终导致内存回收子系统的崩溃

 

posted @ 2023-06-05 21:38  不忘初心2021  阅读(17)  评论(0编辑  收藏  举报