Java的垃圾回收机制是什么?

article/2025/10/8 2:23:04

        说到垃圾回收(Garbage Collection,GC),很多人就会自然而然地把它和Java联系起来。在Java中,程序员不需要去关心内存动态分配和垃圾回收的问题,这一切都交给了JVM来处理。

        顾名思义,垃圾回收就是释放垃圾占用的空间,那么在Java中,什么样的对象会被认定为“垃圾”?那么当一些对象被确定为垃圾之后,采用什么样的策略来进行回收(释放空间)?在目前的商业虚拟机中,有哪些典型的垃圾收集器?

一、如何确定某个对象是“垃圾”?

        如果确定某个对象是“垃圾”?既然垃圾收集器的任务是回收垃圾对象所占的空间供新的对象使用,那么垃圾收集器如何确定某个对象是“垃圾”?—即通过什么方法判断一个对象可以被回收了。

        在java中是通过引用来和对象进行关联的,也就是说如果要操作对象,必须通过引用来进行。那么很显然一个简单的办法就是通过引用计数来判断一个对象是否可以被回收。不失一般性,如果一个对象没有任何引用与之关联,则说明该对象基本不太可能在其他地方被使用到,那么这个对象就成为可被回收的对象了。这种方式成为引用计数法。

        这种方式的特点是实现简单,而且效率较高,但是它无法解决循环引用的问题,因此在Java中并没有采用这种方式(Python采用的是引用计数法)。看下面这段代码:

public class Main {public static void main(String[] args) {MyObject object1 = new MyObject();MyObject object2 = new MyObject();object1.object = object2;object2.object = object1;object1 = null;object2 = null;}
}class MyObject{public Object object = null;
}

        最后面两句将object1和object2赋值为null,也就是说object1和object2指向的对象已经不可能再被访问,但是由于它们互相引用对方,导致它们的引用计数都不为0,那么垃圾收集器就永远不会回收它们。

        为了解决这个问题,在Java中采取了 可达性分析法。该方法的基本思想是通过一系列的“GC Roots”对象作为起点进行搜索,如果在“GC Roots”和一个对象之间没有可达路径,则称该对象是不可达的,不过要注意的是被判定为不可达的对象不一定就会成为可回收对象。被判定为不可达的对象要成为可回收对象必须至少经历两次标记过程,如果在这两次标记过程中仍然没有逃脱成为可回收对象的可能性,则基本上就真的成为可回收对象了。

下面来看个例子:

Object aobj = new Object ( ) ;
Object bobj = new Object ( ) ;
Object cobj = new Object ( ) ;
aobj = bobj;
aobj = cobj;
cobj = null;
aobj = null;

        第几行有可能会使得某个对象成为可回收对象?第7行的代码会导致有对象会成为可回收对象。至于为什么留给读者自己思考。

再看一个例子:

String str = new String("hello");
SoftReference<String> sr = new SoftReference<String>(new String("java"));
WeakReference<String> wr = new WeakReference<String>(new String("world"));

        这三句哪句会使得String对象成为可回收对象?第2句和第3句,第2句在内存不足的情况下会将String对象判定为可回收对象,第3句无论什么情况下String对象都会被判定为可回收对象。

最后总结一下平常遇到的比较常见的将对象判定为可回收对象的情况:

1)显示地将某个引用赋值为null或者将已经指向某个对象的引用指向新的对象,比如下面的代码:

Object obj = new Object();
obj = null;
Object obj1 = new Object();
Object obj2 = new Object();
obj1 = obj2;

2)局部引用所指向的对象,比如下面这段代码:

void fun() {.....for(int i=0;i<10;i++) {Object obj = new Object();System.out.println(obj.getClass());}   
}

循环每执行完一次,生成的Object对象都会成为可回收的对象。

3)只有弱引用与其关联的对象,比如:

WeakReference<String> wr = new WeakReference<String>(new String("world"));

二、典型的垃圾收集算法

        在确定了哪些垃圾可以被回收后,垃圾收集器要做的事情就是开始进行垃圾回收,但是这里面涉及到一个问题是:如何高效地进行垃圾回收。

        由于Java虚拟机规范并没有对如何实现垃圾收集器做出明确的规定,因此各个厂商的虚拟机可以采用不同的方式来实现垃圾收集器,所以在此只讨论几种常见的垃圾收集算法的核心思想。

1、Mark-Sweep(标记-清除)算法

        这是最基础的垃圾回收算法,之所以说它是最基础的是因为它最容易实现,思想也是最简单的。标记-清除算法分为两个阶段:标记阶段和清除阶段。标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。具体过程如下图所示:

​        从图中可以很容易看出标记-清除算法实现起来比较容易,但是有一个比较严重的问题就是容易产生内存碎片,碎片太多可能会导致后续过程中需要为大对象分配空间时无法找到足够的空间而提前触发新的一次垃圾收集动作。

2、Copying(复制)算法

        为了解决Mark-Sweep算法的缺陷,Copying算法就被提了出来。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。具体过程如下图所示:

​        这种算法虽然实现简单,运行高效且不容易产生内存碎片,但是却对内存空间的使用做出了高昂的代价,因为能够使用的内存缩减到原来的一半。

        很显然,Copying算法的效率跟存活对象的数目多少有很大的关系,如果存活对象很多,那么Copying算法的效率将会大大降低。

3、Mark-Compact(标记-整理)算法

        为了解决Copying算法的缺陷,充分利用内存空间,提出了Mark-Compact算法。该算法标记阶段和Mark-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。具体过程如下图所示:

4、Generational Collection(分代收集)算法

        分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。

        目前大部分垃圾收集器对于新生代都采取Copying算法,因为新生代中每次垃圾回收都要回收大部分对象,也就是说需要复制的操作次数较少,但是实际中并不是按照1:1的比例来划分新生代的空间的,一般来说是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor空间中,然后清理掉Eden和刚才使用过的Survivor空间。

        而由于老年代的特点是每次回收都只回收少量对象,一般使用的是Mark-Compact算法。

        注意,在堆区之外还有一个代就是永久代(Permanet Generation),它用来存储class类、常量、方法描述等。对永久代的回收主要回收两部分内容:废弃常量和无用的类。

三、典型的垃圾收集器

        垃圾收集算法是 内存回收的理论基础,而垃圾收集器就是内存回收的具体实现。下面介绍一下HotSpot(JDK 7)虚拟机提供的几种垃圾收集器,用户可以根据自己的需求组合出各个年代使用的收集器。

1、Serial/Serial Old

        Serial/Serial Old收集器是最基本最古老的收集器,它是一个单线程收集器,并且在它进行垃圾收集时,必须暂停所有用户线程。Serial收集器是针对新生代的收集器,采用的是Copying算法,Serial Old收集器是针对老年代的收集器,采用的是Mark-Compact算法。它的优点是实现简单高效,但是缺点是会给用户带来停顿。

2、ParNew

        ParNew收集器是Serial收集器的多线程版本,使用多个线程进行垃圾收集。

3、Parallel Scavenge

        Parallel Scavenge收集器是一个新生代的多线程收集器(并行收集器),它在回收期间不需要暂停其他用户线程,其采用的是Copying算法,该收集器与前两个收集器有所不同,它主要是为了达到一个可控的吞吐量。

4、Parallel Old

        Parallel Old是Parallel Scavenge收集器的老年代版本(并行收集器),使用多线程和Mark-Compact算法。

5、CMS

        CMS(Current Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,它是一种并发收集器,采用的是Mark-Sweep算法。

6、G1

        G1收集器是当今收集器技术发展最前沿的成果,它是一款面向服务端应用的收集器,它能充分利用多CPU、多核环境。因此它是一款并行与并发收集器,并且它能建立可预测的停顿时间模型。

下面补充一下关于内存分配方面的东西:

​        对象的内存分配,往大方向上讲就是在堆上分配,对象主要分配在新生代的Eden Space和From Space,少数情况下会直接分配在老年代。

        如果新生代的Eden Space和From Space的空间不足,则会发起一次GC,如果进行了GC之后,Eden Space和From Space能够容纳该对象就放在Eden Space和From Space。在GC的过程中,会将Eden Space和From Space中的存活对象移动到To Space,然后将Eden Space和From Space进行清理。

        如果在清理的过程中,To Space无法足够来存储某个对象,就会将该对象移动到老年代中。在进行了GC之后,使用的便是Eden space和To Space了,下次GC时会将存活对象复制到From Space,如此反复循环。当对象在Survivor区躲过一次GC的话,其对象年龄便会加1,默认情况下,如果对象年龄达到15岁,就会移动到老年代中。

        一般来说,大对象会被直接分配到老年代,所谓的大对象是指需要大量连续存储空间的对象,最常见的一种大对象就是大数组,比如:

byte[] data = new byte[410241024]

        这种一般会直接在老年代分配存储空间。

        当然分配的规则并不是百分之百固定的,这要取决于当前使用的是哪种垃圾收集器组合和JVM的相关参数。

Java学习视频

Java基础:

Java300集,Java必备优质视频_手把手图解学习Java,让学习成为一种享受

Java项目:

【Java游戏项目】1小时教你用Java语言做经典扫雷游戏_手把手教你开发游戏

【Java毕业设计】OA办公系统项目实战_OA员工管理系统项目_java开发


http://chatgpt.dhexx.cn/article/1grNN487.shtml

相关文章

GC垃圾回收机制

GC概述 垃圾回收是一种自动的存储管理机制。当一些被占用的内存不再需要时&#xff0c;将其释放&#xff0c;以让出空间&#xff0c;这种存储管理机制&#xff0c;就叫做垃圾回收机制。可以很大程度减轻程序员的负担和错误。 通常在堆(heap) 和 元空间(metaspace)需要GC机制。…

python垃圾回收机制

python垃圾回收机制 现在的高级语言如java&#xff0c;c#等&#xff0c;都采用了垃圾收集机制&#xff0c;而不再是c&#xff0c;c里用户自己管理维护内存的方式。自己管理内存极其自由&#xff0c;可以任意申请内存&#xff0c;但如同一把双刃剑&#xff0c;为大量内存泄露&a…

Java 垃圾回收机制原理

最近做一个ETL的项目模块&#xff0c;经常由于查询数据量比较大用消息中间件MQ时引起了内存溢出的报错。做完后没事研究了一下JVM和垃圾回收的相关知识点。 一&#xff1a;垃圾回收机制的意义 java 语言中一个显著的特点就是引入了java回收机制&#xff0c;是c程序员最头疼的…

五、垃圾回收机制

一 引入 解释器在执行到定义变量的语法时&#xff0c;会申请内存空间来存放变量的值&#xff0c;而内存的容量是有限的&#xff0c;这就涉及到变量值所占用内存空间的回收问题&#xff0c;当一个变量值没有用了&#xff08;简称垃圾&#xff09;就应该将其占用的内存给回收掉&…

一、垃圾回收机制

. 一、Java内存结构1、Java堆&#xff08;Java Heap&#xff09;2、Java虚拟机栈&#xff08;Java Virtual Machine Stacks&#xff09;3、本地方法栈&#xff08;Native Method Stack&#xff09;4、方法区&#xff08;Method Area&#xff09;5、执行引擎 二、垃圾回收机制1、…

垃圾回收机制浅聊

对于任何一门语言而言&#xff0c;在运行过程中都会创建许多对象&#xff0c;继而需要为这些对象分配内存地址&#xff0c;当这些对象不需要使用的时候&#xff0c;需要释放其占用的内存地址&#xff0c;以供新的对象使用。关于对象内存释放的这一机制就叫做垃圾回收机制&#…

图解Java 垃圾回收机制

摘要&#xff1a; Java技术体系中所提倡的 自动内存管理 最终可以归结为自动化地解决了两个问题&#xff1a;给对象分配内存 以及 回收分配给对象的内存&#xff0c;而且这两个问题针对的内存区域就是Java内存模型中的 堆区。关于对象分配内存问题&#xff0c;笔者的博文《JVM …

深入理解 Java 垃圾回收机制

一&#xff1a;垃圾回收机制的意义 java 语言中一个显著的特点就是引入了java回收机制&#xff0c;是c程序员最头疼的内存管理的问题迎刃而解&#xff0c;它使得java程序员在编写程序的时候不在考虑内存管理。由于有个垃圾回收机制&#xff0c;java中的额对象不在有“作用域”…

JVM垃圾回收机制

文章目录 JVM垃圾回收机制一、垃圾回收原理二、对象被标记为垃圾方法1. 引用计数器2. 可达性分析 三、垃圾回收算法1. 标记-清除算法2. 复制算法3. 标记-整理算法4. 分代回收算法 JVM垃圾回收机制 一、垃圾回收原理 GC (Garbage Collection:即垃圾回收)的基本原理&#xff1a…

Java垃圾回收(GC)机制

一、为什么要垃圾回收 如果不进行垃圾回收&#xff0c;内存迟早都会被消耗空&#xff0c;因为我们在不断的分配内存空间而不进行回收。除非内存无限大&#xff0c;我们可以任性的分配而不回收&#xff0c;但是事实并非如此。所以&#xff0c;垃圾回收是必须的。 哪些内存需要…

JVM:常用的四种垃圾回收机制

1.CMS (Concurrent Mark Sweep)&#xff1a;并行 标记清除 老年代垃圾回收机制 cms是一个基于标记-清除 算法的综合多种算法的老年代垃圾回收器 适用场景&#xff1a;重视服务器响应速度&#xff0c;要求系统停顿时间最短。 这里要说明下&#xff0c;这是一个老年代算法&…

什么是垃圾回收机制(超详细)

垃圾回收机制 1.垃圾回收机制&#xff08;Garbage Collction&#xff09;简称GC&#xff0c;是JavaScript中使用的内存管理系统的基本组部分&#xff0c;是为了防止内存泄漏 2.JavaScript是在创建变量&#xff08;对象、字符串等&#xff09;时自动进行了分配内存&#xff0c;并…

深入理解JVM—垃圾回收机制

一、前言 明确垃圾收集器关注的部分&#xff1a;堆和方法区。着重学习如何确定哪些垃圾需要回收、垃圾回收算法以及GC触发条件。 二、如何确定哪些垃圾需要回收 1、引用计数算法 在对象中添加一个引用计数器&#xff0c;每当有一个地方引用它时&#xff0c;计数器值就加一&…

JavaScript垃圾回收机制

JavaScript垃圾回收机制 1 垃圾为何要产生并回收2 垃圾回收机制2.1 标记清除法2.2 引用计数法 3 V8对垃圾回收机制的优化——分代式垃圾回收机制3.1 新生代与老生代3.2 新生代的垃圾回收3.3 老生代的垃圾回收 1 垃圾为何要产生并回收 当我们写代码时创建一个基本类型、对象、函…

mysql初学——“[ERROR] [MY-012271] [InnoDB] The innodb_system data file 'ibdata1' must be writable”

这个问题一般会在安装后&#xff0c;想要启动mysql的时候出现。之前在网上查到过处理这类问题的解决方案&#xff0c;是删除一些文件&#xff0c;后面发现没有用&#xff0c;并且导致其他一些问题的出现。在stackoverflowed网站中发现一种解释是mysql已经运行了&#xff0c;发现…

MySQL的ibdata1文件无了该怎么恢复

前段时间电脑突然坏了&#xff0c;系统盘全部格式化了&#xff0c;只剩data源文件&#xff0c;装上MySQL把源文件放进去后发现所有库的表打不了开了&#xff0c; 我的mysql是5.7的版本&#xff0c;看网上说是ibdata1文件的问题&#xff0c;以前的ibdata1文件已经没了&#xff…

windows mysql 启动失败 :The innodb_system data file ‘ibdata1‘ must be writable

net start mysql 启动失败 在安装目录如&#xff1a;D:\mysql8\bin 下按住shift点击右键&#xff0c;启动cmd或者PowerShell 执行命令&#xff1a;mysqld --console 查看日志 注意error级别的记录&#xff1a;The innodb_system data file ‘ibdata1’ must be writable 进入…

MySQL 5.6 如何给ibdata1瘦身

前不久刚给ibdata1瘦身&#xff0c;发篇文章总结下。 ibdata1是MySQL使用InnoDB引擎时所产生的文件&#xff0c;其一般存储数据、索引、结构、缓冲数据、共享数据和重做日志等。因为ibdata1只增不减&#xff0c;长期操作数据库&#xff0c;可能会使其越来越大&#xff0c;而浪费…

误删mysql8下ibdata1文件恢复数据

一位小伙伴不小心误删了生产环境上mysql数据下/data/mysql/ibdata1和ib_logfile0、ib_logfile1文件&#xff0c;并且mysql服务停止了&#xff0c;造成mysql重启不了&#xff0c;吓得小伙伴以为要“被删库跑路”了&#xff0c;于是赶紧帮忙”救火”。像这种误删数据或者删库的情…

MySQL ibdata1 文件“减肥”记

夏天来了&#xff0c;没想到连 ibdata1 文件也要开始“减肥”了~ 作者&#xff1a;杨彩琳 爱可生华东交付部 DBA&#xff0c;主要负责 MySQL 日常问题处理及 DMP 产品支持。爱好跳舞&#xff0c;追剧。 本文来源&#xff1a;原创投稿 有句话是这么说的&#xff1a;“在 InnoDB…