JVM基础(三):垃圾回收机制

本文详细介绍了JVM的垃圾回收机制,包括需要回收的内存区域(主要集中在堆区)、垃圾识别的引用计数法和可达性分析法、四种不同类型的引用以及垃圾收集的不同算法,如分代收集、标记-清除、标记-复制和标记-整理。文章还讨论了被标记为垃圾的对象不一定被回收以及调用`System.gc()`不一定触发回收的现象,帮助读者理解JVM内存管理的关键点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

很高兴遇见你~ 欢迎阅读我的文章。

Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来

垃圾收集(Garbage Collection ,也称为GC),是虚拟机中一个永恒不变的话题。上述那句话堪称经典,同时也点出了垃圾回收这个问题的重要性。在c/c++中,开发者对内存有至高无上的权利,同时也需要自己对对象负责到底:创建和释放,每一块内存使用完之后都需要调用free方法来释放内存。而JVM采用自动化技术,开发者无需关心内存的分配以及释放,当一个对象不再使用时,垃圾回收机制会这个对象进行回收。然而,这一切看起来很美好的垃圾回收机制,背后却“暗藏杀机”。垃圾回收机制并不是完美的,当因开发者使用不当或者其他原因出现的内存泄漏或内存溢出问题时,如果对JVM的垃圾回收机制不了解,那么很难去排查问题。因而,学习垃圾回收机制不仅是需要应付面试,更重要的是能够让自己写出更加健壮的代码、解决垃圾回收机制带来的问题。

垃圾回收机制有四个关键问题:

  1. 什么是垃圾回收机制?
  2. 回收哪个区域的垃圾?
  3. 什么时候回收?
  4. 如何回收?

这四个问题可以说是垃圾回收机制的核心,弄懂这四个问题,也就了解了垃圾回收机制。关于第一个问题上面已经有讲述了,总的来说就是虚拟机中一套可以自动对不再使用的对象进行回收释放内存的机制 。那么下面,将围绕另外三个问题来展开讲解。

哪些内存需要回收

JVM基础(二):运行时数据区一文中讲到JVM的运行时数据区主要有线程私有的方法栈和程序计数器和线程共享的堆区和方法区。

方法栈和程序计数器随着线程生而生,同时也随着线程亡而亡。每个方法被调用时会压入一个栈帧,每个栈帧的大小在编译阶段也已经确定,随着方法的调用与返回,栈帧有条不紊地执行入栈与出栈操作,这部分的内存分配和回收具有确定性,因为不需要进行垃圾回收。而相对的,Java堆和方法区则不确定性非常高。在运行时,方法区需要加载的类、常量的添加、对象的创建,这些都充满了不确定性,无法在编译阶段确定下来。因而,程序在运行时会在这两个区域不断进行创建对象。对这两个区域内存进行管理就显得非常重要。

而在这两个区域中,堆区的管理显得比方法区更加重要且常见。方法区的垃圾回收事实上非常少,且可以回收的时机非常苛刻,如类的卸载。而堆区是对象实例的存储地,程序在运行时会频繁进行创建然后丢弃对象,因而下面我也主要围绕堆区来展开讲述垃圾回收。

方法区有必要进行回收吗

有。

方法区存储的数据,貌似是在整个程序运行期间都必须存在的数据:类信息、常量、各种符号引用等。但,事实上并不是。但是这些需要回收场景相对较特殊且少见,很难引起注意,而这往往也是内存泄漏的隐患所在。方法区需要回收的对象少,回收条件苛刻,以至于回收性价比低下,在一些虚拟机也并没有实现方法区的回收机制,在Java虚拟机规范中也没有明确要求实现方法区的垃圾收集。但,很难,并不意味着就可以不做。在一些场景下,对方法区的回收还是非常有必要的。

方法区回收的数据主要有两种:类信息、常量。不同的类加载器加载同一个类在虚拟机角度来看是属于完全不同的两个类,而在一些频繁使用字节码操作、大量反射、动态代理等等的情景下,加载进来的类信息非常多却通常只需要创建一个对象就不再使用了,那么就很有必要对这些类信息进行回收来减少方法区的压力,也就是,类卸载。同样,常量也并不是在整个程序运行期间都需要使用到,对于一些不再使用的常量也可以进行回收。那么如何判断一个类或者一个常量需要被回收呢?

常量相对来说较为简单,废弃的常量或者不再使用的常量可以被回收。怎么判断?在虚拟机任何地方都没有引用这个常量,那么他就是个废弃的常量了。

类信息比较复杂,需要符合三个条件:

  1. 该类包括子类所有的对象实例全部被回收。
  2. 加载该类的类加载器被回收。
  3. 该类的Class对象没有被引用。

这个时候,就可以对这个类进行卸载了。事实上我们很多的类都是使用应用类加载器或者系统类加载器来加载,那么第二个条件就很难去满足了。所以一般情况下,自定义类加载器加载的类才有更大的可能被回收。关于类加载器的内容后面再讲。

如何识别垃圾

前面讲到,垃圾回收主要的区域是Java堆区,而这个区域主要存储的是对象实例。要进行垃圾回收,首先要判断一个对象是否是垃圾,那么如何判断一个对象是否是垃圾呢?这里有两种很常见的解决方法:引用计数法、可达性分析法。

引用计数法

引用计数法可能是读者最先认识的一种垃圾标记法,也是最为被广泛认知的一种算法。引用计数法的概念相对简单:

使用一个额外的内

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值