判断对象是否存活的算法
需要回收的位置如下
其实垃圾回收是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虚拟机内部情况的JMXBean、 JVMTI中注册的回调、 本地代码缓存等
这里可达性分析算法是java判断对象存活使用的
但是这个时候的对象还并不能进入死亡,还需要再次的判断
下面通过模拟的程序进行说明
代码部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | 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依赖
1 2 3 4 5 6 | <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()执行缓慢,会有发生死循环的可能性,甚至最终导致内存回收子系统的崩溃
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)