JVM 怎么判断对象已经死了?

简介: 在Java中程序计数器、虚拟机栈、本地方法栈这三个区域随线程而生,随线程而灭:栈中的栈帧随着方法的调用和退出而有条不紊的进行着入栈和出栈的过程。

GC的历史比Java还有久远,我们在思考GC时候需要思考三个问题:


哪些内存需要回收?


什么时候回收?


如何回收?


在Java中程序计数器、虚拟机栈、本地方法栈这三个区域随线程而生,随线程而灭:栈中的栈帧随着方法的调用和退出而有条不紊的进行着入栈和出栈的过程。


每个栈帧分配多少内存在类结构确定下来时就已知的,方法结束或者线程结束内存自然跟着回收了。


而Java堆和方法区不一样,一个接口中的多个实现类的内存可能不一样,每个方法的多个分支需要的内存也可能不一样,我们只有在程序运行时候才知道会创建哪些对象,这部分内存的分配和回收都是动态的。


一、判断对象已死的算法


1)引用计数算法


给对象添加一个引用计数器,每当一个地方引用它时候,计数器就加1,当引用失效,计数器就减1;任何时刻计数器为0的对象就是不可能再被使用了。


这种方法实现简单,效率高,但是它很难解决对象的循环引用问题:

public class Test {
    private static final int _1MB = 1024 *1024;
    private Object instance = null;
    public static void testGC(){
        Test objectA = new Test();
        Test objectB = new Test();
        objectA.instance = objectB;
        objectB.instance = objectA;
        objectA = null;
        objectB = null;
        // 假如这里发生GC,objectA和objectB会不会被回收
    }
}

2)可达性分析算法


这个算法的基本思路是通过一系列称为“GC Roots”(一组必须活跃的引用)作为起始点,从这些节点开始向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链时候,那么证明此对象是不可用的。


在Java语言中,做作为GC Roots的对象包括以下几种:


1)虚拟机栈(栈帧中的本地变量表)中引用的对象。


2)方法区中类静态属性引用的对象。


3)方法区中常量引用的对象。


4)本地方法栈JNI(即一般说的Native方法)引用的对象。


二、引用


无论是通过引用计数器算法判断对象的引用数量,还是通过可达性分析算法判断对象引用链是否可达,判断对象是否可活都离不开引用,Java中将引用分为四种:


1)强引用(Strong Reference)


是指程序代码中普遍存在的,类似“Object obj = new Object()”这类的引用,只有强引用还存在对象就不会被回收。


2)软引用(Soft Reference)


软引用是用来描述一些还有用但是非必须的对象。对于软引用关联的对象,在系统将于发生内存溢出异常之前,将会把这些对象列进回收范围中进行二次回收。


3)弱引用(Weak Reference)


也是用来描述非必须对象的,强度比软引用还弱一些,被软引用关联的对象只能存活到下一次内存回收之前。


4)虚引用(Phantom Referenece)


也成为幽灵引用和幻影引用,为一个对象设置虚引用关联的唯一目的就是能在这个对象被回收时收到一个系统通知。


三、生存还是死亡


即使在可达性分析算法中不可达的对象,也并非一定是“非死不可”的,这时候他们暂时处于“缓刑”阶段,真正宣告一个对象死亡至少要经历两个阶段:


1)如果对象在可达性分析算法中不可达,那么它会被第一次标记并进行一次刷选,刷选的条件是是否需要执行finalize()方法(当对象没有覆盖finalize()或者finalize()方法已经执行过了(对象的此方法只会执行一次)),虚拟机将这两种情况都会视为没有必要执行)。


2)如果这个对象有必要执行finalize()方法会将其放入F-Queue队列中,稍后GC将对F-Queue队列进行第二次标记,如果在重写finalize()方法中将对象自己赋值给某个类变量或者对象的成员变量,那么第二次标记时候就会将它移出“即将回收”的集合。


finalize()能做的工作,使用try-finally或者其他方式都能做到更好,更及时,所以不建议使用此方法。


四、方法区回收


永久代中回收的内容主要是两部分:废弃的常量和无用的类。判断无用的类(类卸载)必须满足三个条件:


1)该类所以的实例都已经被回收


2)加载该类的ClassLoader被回收


3)该类对应的java.lang.Class对象没有在任何地方引用,无法在任何地方通过反射访问该类的方法


相关文章
|
7月前
|
存储 安全 算法
深入剖析JVM内存管理与对象创建原理
JVM内存管理,JVM运行时区域,直接内存,对象创建原理。
115 2
|
7月前
|
存储 算法 安全
【JVM】深入理解JVM对象内存分配方式
【JVM】深入理解JVM对象内存分配方式
98 0
|
1月前
|
存储 算法 Java
散列表的数据结构以及对象在JVM堆中的存储过程
本文介绍了散列表的基本概念及其在JVM中的应用,详细讲解了散列表的结构、对象存储过程、Hashtable的扩容机制及与HashMap的区别。通过实例和图解,帮助读者理解散列表的工作原理和优化策略。
41 1
散列表的数据结构以及对象在JVM堆中的存储过程
|
4天前
|
缓存 Java
JVM对象引用
本次课程聚焦JVM对象引用,涵盖强引用、软引用、弱引用和虚引用。强引用是最常见的引用类型,确保对象不会被垃圾回收器回收,适用于需要确保对象存活的场景;软引用在内存不足时会被优先回收,常用于缓存;弱引用的对象随时可能被回收,适合临时对象;虚引用最弱,主要用于接收对象回收通知,进行资源清理。通过合理选择引用类型,可优化内存管理,避免内存泄露。
|
2月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
89 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
2月前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
62 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
|
2月前
|
算法 Java
JVM进阶调优系列(3)堆内存的对象什么时候被回收?
堆对象的生命周期是咋样的?什么时候被回收,回收前又如何流转?具体又是被如何回收?今天重点讲对象GC,看完这篇就全都明白了。
|
4月前
|
存储 Java 程序员
Java中对象几种类型的内存分配(JVM对象储存机制)
Java中对象几种类型的内存分配(JVM对象储存机制)
96 5
Java中对象几种类型的内存分配(JVM对象储存机制)
|
5月前
|
存储 监控 算法
(六)JVM成神路之GC基础篇:对象存活判定算法、GC算法、STW、GC种类详解
经过前面五个章节的分析后,对于JVM的大部分子系统都已阐述完毕,在本文中则开始对JVM的GC子系统进行全面阐述,GC机制也是JVM的重中之重,调优、监控、面试都逃不开的JVM话题。
174 8
|
5月前
|
存储 缓存 算法
(五)JVM成神路之对象内存布局、分配过程、从生至死历程、强弱软虚引用全面剖析
在上篇文章中曾详细谈到了JVM的内存区域,其中也曾提及了:Java程序运行过程中,绝大部分创建的对象都会被分配在堆空间内。而本篇文章则会站在对象实例的角度,阐述一个Java对象从生到死的历程、Java对象在内存中的布局以及对象引用类型。
140 8