Java基础之垃圾回收机制详解

一、GC的作用

进行内存管理

C语言中的内存,申请内存之后需要手动释放;一旦忘记释放,就会发生内存泄漏!

而Java语言中,申请内存后会由GC来释放内存空间,无需手动释放

GC虽然代替了手动释放的操作,但是它也有局限性:

  • 需要消耗更多的资源;
  • 没有手动释放那么及时;
  • STW(Stop The World)会影响程序的执行效率

二、GC主要回收哪些内存

(1)堆:主要回收堆中的内存

(2)方法区:需要回收

(3)栈(包括本地方法栈和JVM虚拟机栈):不需要回收,栈上的内存什么时候释放是明确的(线程结束,栈上的内存也就被释放了;对应的某个栈帧销毁[某个方法执行完毕],也会导致对应的局部变量被释放)

(4)程序计数器:不需要被回收

GC回收内存的基本单位:对象

GC回收对象的基本思路

(1)标记:判断当前对象的生死,对象不再被使用为死,则需要回收,反之不需要被回收;

标记的方法:

  • 引用计数法

记录当前这个对象是否有引用指向,有则引用计数加1,如果当前这个对象的引用指向了其他新的对象,则引用计数减1,当引用计数为0的时候,我们认为这个对象需要被回收!

缺点:无法解决循环引用问题

下面用一段伪代码来演示一下循环引用问题:

class Test{
Test t = null;
}
Test a = new Test();
Test b = new Test();
a.t = b;
b.t = a;
a = null;
b = null;

我们发现,在上述代码中已经没有办法使用对象a和对象b了,但是它们的引用计数不为1.想使用对象a,就得找到对象a的引用,但是对象a的引用又在对象b当中。想使用对象b,就得找到对象b 的引用,但是对象b的引用又在对象a当中。

  • 可达性分析:

代码中的对象具有一定的关联关系,这样错综复杂的关系,构成了一个"有向图"。可达性分析也就是遍历这个对象关系的“有向图”。如果某个对象可以被遍历到,那么它就是可达的(非垃圾),那么就是不可达的(是垃圾)

那么可达性分析从哪里开始呢?

a)针对每个线程的每个栈帧的局部变量表(线程有很多,每个线程栈帧也有很多,每个栈帧也会有很多个变量);

b)常量池中引用的对象;

c)方法区中静态变量引用的对象;

因为遍历的起点不止一个,而是很多个起点,因此把这些起点也称之为GCRoot

  • 回收方法区对象的规则:

a)该类的所有实例已经被回收;

b)加载类的ClassLoader也已经被回收了;

c)该类对象没有在代码中使用了

同时具备以上三个条件,就认为该类对象是可以被回收的

回收的方法:

  • 标记-清除【适合老年代】

通过上面的图,我们可以发现,两个空闲区被其他的对象分隔开了。一旦需要一个比较大的空间,就会申请失败。

标记-清除法的优缺点:

优点:简单高效

缺点:会出现内存碎片

  • 标记-复制【适合新生代】

优点:解决了内存碎片问题,保证回收之后不会存在碎片(回收后使用的对象之间是连续的,空余内存之间也是连续的)

缺点:需要一块额外的空间,如果生存的对象较多就比较难低效

  • 标记-整理【适合老年代】

优点:没有内存碎片问题,也不需要额外的空间

缺点:类似于顺序表的删除操作,效率不是很高

三、分代回收

按照对象的年龄,将堆内存分为:新生代(伊甸区和生存区)、老年代

对象的年龄不是直接使用时间来记录,而是使用对象活过GC轮次来记录(GC是按照一定周期来运行)

一个对象的一生:

(1)对象诞生于新生代的伊甸区。新产生的对象的内存就是新生代中的内存

(2)第一轮GC扫描伊甸区之后,就会把大量的对象回收掉。少数没有被回收的对象,就会通过标记-复制算法进入到生存区

(3)少数进入生存区的对象,再次被GC扫描(对这些对象进行可达性分析)。如果发现该对象已经不可达,也就被销毁了。没有被销毁的对象,再次通过标记-复制算法,把它拷贝到另一个生存区。

(4)对象在两个生存区中经过若干次拷贝,如果还没有被回收,那么就说明这些个对象存活时间比较久,就拷贝到老年代

(5)老年代的对象也是要经过GC扫描的。由于老年代的对象生存时间比较长。因此扫描周期要比新生代的周期要长

相关术语:

  • Partical GC:只进行一部分内存区域的GC
  • Full GC:针对整个内存区域进行GC
  • Minor GC:针对新生代内存的GC,执行频繁,速度较快
  • Major GC:针对老年代的GC,没那么频繁。速度较慢,通常由Minor GC 触发

四、垃圾回收器

垃圾回收器做的两件事情:标记(可达性分析)+回收(标记清除,标记复制,标记整理)

  • Serial收集器(给新生代使用,串行回收)【存在STW】

采用复制算法,单线程进行标记和回收

  • ParNew收集器(新生代收集器,多线程GC)

采用复制算法,多线程进行标记和回收

  • Parallel scavenge收集器(新生代收集器,并行GC)

设计初衷是为了缩短STW时间,以牺牲吞吐量和新生代空间作为代价。

相当于承诺用户,在一定时间内就会完成一次GC。

  • Serial Old收集器(老年代收集器,串行GC)

  • Parallel old收集器(老年代收集器,并行GC)

使用多线程完成标记整理,效率更高,消耗的CPU资源更多

  • CMS垃圾回收器(老年代收集器,并行GC,采用多线程标记清除算法)

a)初始标记【STW】
只是把和GCRoot相关的对象标记出来,涉及STW
b)并发标记
执行整个标记遍历的过程(从GCRoot开始,把能访问的对象都遍历)
不需要暂停用户线程

消耗的时间相对比较久,但是可以和用户线程并发

注意:当进行并发标记的时候,当用户线程也在执行,可能导致某个对象,刚刚标记的时候不是垃圾,代码执行后,就成了垃圾

c)重新标记(CMS remark)【STW】
修正误差

d)并发清除
多线程的方式将刚刚的垃圾对象都清除释放掉,可以和应用程序并发执行

优点:能够让STW时间尽量短
缺点:有内存碎片; GC操作和应用程序并发进行,消耗CPU资源多;

  • G1回收器(Java11开始默认使用)

既可以回收新生代,也可以回收老年代

每个矩形称为一个region
E表示伊甸区
S表示生存区
T表示老年代
H表示存放大对象的区域
以region为单位进行回收,回收粒度更精细
针对新生区的region同样适用复制算法
针对老年代的回收类似于CMS
a)初始标记【STW】:只去找和GRoot直接相连的对象
b)并发标记:和应用程序并发执行,进行可达性分析,遍历所有对象。如果发现某个老年代region中已经没有存活对象,就直接回收
c)最终标记:修正第二步产生的误差
d)筛选回收:挑选出对象存活率低的region进行回收

五、总结

到此这篇关于Java基础之垃圾回收机制详解的文章就介绍到这了,更多相关Java垃圾回收机制内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java 垃圾回收机制详解及实例代码

     Java 垃圾回收机制详解 乍一看,垃圾回收所做的事情应当恰如其名--查找并清除垃圾.事实上却恰恰相反.垃圾回收会跟踪所有仍在使用的对象,然后将剩余的对象标记为垃圾.牢记了这点之后,我们再来深入地了解下这个被称为"垃圾回收"的自动化内存回收在JVM中到底是如何实现的. 手动管理内存 在介绍现代版的垃圾回收之前,我们先来简单地回顾下需要手动地显式分配及释放内存的那些日子.如果你忘了去释放内存,那么这块内存就无法重用了.这块内存被占有了却没被使用.这种场景被称之为内存泄露. 下面是用C写

  • Java垃圾回收机制的finalize方法实例分析

    本文实例讲述了Java垃圾回收机制的finalize方法.分享给大家供大家参考,具体如下: 一 点睛 finalize方法有如下四个特点: 永远不要主动调用某个对象的finalize方法,该方法应交给垃圾回收机制调用. finalize方法的何时被调用,是否被调用具有不确定性.不要把finalize方法当成一定会被执行的方法. 当JVM执行可恢复对象的finalize方法时,可能使该对象或系统中其他对象重新变成可达状态. 当JVM执行finalize方法时出现了异常,垃圾回收机制不会报告异常,程

  • Java文件流关闭和垃圾回收机制

    1.先看以下一段代码 import java.io.FileInputStream; public class TTT { public static void main(String[] args) throws Exception { for (int i = 0; i < 10; i++) { final String threadId = "thread_" + i; Thread thread = new Thread(new Runnable() { public v

  • 老生常谈Java虚拟机垃圾回收机制(必看篇)

    在Java虚拟机中,对象和数组的内存都是在堆中分配的,垃圾收集器主要回收的内存就是再堆内存中.如果在Java程序运行过程中,动态创建的对象或者数组没有及时得到回收,持续积累,最终堆内存就会被占满,导致OOM. JVM提供了一种垃圾回收机制,简称GC机制.通过GC机制,能够在运行过程中将堆中的垃圾对象不断回收,从而保证程序的正常运行. 垃圾对象的判定 我们都知道,所谓"垃圾"对象,就是指我们在程序的运行过程中不再有用的对象,即不再存活的对象.那么怎么来判断堆中的对象是"垃圾&q

  • Java垃圾回收机制算法详解

    概述 Java GC(Garbage Collection,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代码,对内存泄露和溢出的问题,也不需要像C程序员那样战战兢兢.这是因为在Java虚拟机中,存在自动内存管理和垃圾清扫机制.概括地说,该机制对JVM中的内存进行标记,并确定哪些内存需要回收,根据一定的回收策略,自动的回收内存,永不停息的保证JVM中的内存空间,防止出现内存泄露和溢出问题. 在真实工作中的项目中,时不时的会发生内存溢

  • Java的垃圾回收机制实例分析

    本文实例讲述了Java的垃圾回收机制.分享给大家供大家参考,具体如下: 一 点睛 当程序创建对象.数组等引用类型实体时,系统都会在堆内存中为之分配一块内存区,对象就保存在这块内存区中,当这块内存不再被任何引用变量引用时,这块内存就变成了垃圾,等待垃圾回收机制进行回收. 垃圾回收机制的特点: 垃圾回收机制只负责回收堆内存中对象,不会回收任何任何物理资源(例如数据库连接,网络IO等资源). 程序无法精确控制垃圾回收的运行,垃圾回收会在合适时候进行垃圾回收.当对象永久性地失去引用后,系统就会在合适时候

  • 快速了解JAVA垃圾回收机制

    说到垃圾回收(Garbage Collection,GC),很多人就会自然而然地把它和Java联系起来.在Java中,程序员不需要去关心内存动态分配和垃圾回收的问题,这一切都交给了JVM来处理.顾名思义,垃圾回收就是释放垃圾占用的空间,那么在Java中,什么样的对象会被认定为"垃圾"?那么当一些对象被确定为垃圾之后,采用什么样的策略来进行回收(释放空间)?在目前的商业虚拟机中,有哪些典型的垃圾收集器?下面我们就来逐一探讨这些问题.以下是本文的目录大纲: 如果有不正之处,希望谅解和批评指

  • Java垃圾回收机制简述

    说到垃圾回收(Garbage Collection,GC),很多人就会自然而然地把它和Java联系起来.在Java中,程序员不需要去关心内存动态分配和垃圾回收的问题,这一切都交给了JVM来处理. 顾名思义,垃圾回收就是释放垃圾占用的空间,那么在Java中,什么样的对象会被认定为"垃圾"?那么当一些对象被确定为垃圾之后,采用什么样的策略来进行回收(释放空间)?在目前的商业虚拟机中,有哪些典型的垃圾收集器?下面我们就来逐一探讨这些问题.以下是本文的目录大纲: 如何确定某个对象是"

  • Java 垃圾回收机制详解(动力节点Java学院整理)

    1. 垃圾回收的意义 在C++中,对象所占的内存在程序结束运行之前一直被占用,在明确释放之前不能分配给其它对象:而在Java中,当没有对象引用指向原先分配给某个对象的内存时,该内存便成为垃圾.JVM的一个系统级线程会自动释放该内存块.垃圾回收意味着程序不再需要的对象是"无用信息",这些信息将被丢弃.当一个对象不再被引用的时候,内存回收它占领的空间,以便空间被后来的新对象使用.事实上,除了释放没用的对象,垃圾回收也可以清除内存记录碎片.由于创建对象和垃圾回收器释放丢弃对象所占的内存空间,

  • 简单介绍Java垃圾回收机制

    Java的内存分配与回收全部由JVM垃圾回收进程自动完成.与C语言不同,Java开发者不需要自己编写代码实现垃圾回收.这是Java深受大家欢迎的众多特性之一,能够帮助程序员更好地编写Java程序. 这篇教程是系列第一部分.首先会解释基本的术语,比如JDK.JVM.JRE和HotSpotVM.接着会介绍JVM结构和Java堆内存结构.理解这些基础对于理解后面的垃圾回收知识很重要. Java关键术语 JavaAPI:一系列帮助开发者创建Java应用程序的封装好的库. Java开发工具包(JDK):一

  • 简单理解Java的垃圾回收机制与finalize方法的作用

    垃圾回收器要回收对象的时候,首先要调用这个类的finalize方法(你可以 写程序验证这个结论),一般的纯Java编写的Class不需要重新覆盖这个方法,因为Object已经实现了一个默认的,除非我们要实现特殊的功能(这 里面涉及到很多东西,比如对象空间树等内容). 不过用Java以外的代码编写的Class(比如JNI,C++的new方法分配的内存),垃圾回收器并不能对这些部分进行正确的回收,这时就需要我们覆盖默认的方法来实现对这部分内存的正确释放和回收(比如C++需要delete). 总之,f

  • 深入理解Java垃圾回收机制以及内存泄漏

    前言 在segmentfault上看到一个问题:java有完善的GC机制,那么在java中是否会出现内存泄漏的问题,以及能否给出一个内存泄漏的案例.本问题视图给出此问题的完整答案. 垃圾回收机制简介 在程序运行过程中,每创建一个对象都会被分配一定的内存用以存储对象数据.如果只是不停的分配内存,那么程序迟早面临内存不足的问题.所以在任何语言中,都会有一个内存回收机制来释放过期对象的内存,以保证内存能够被重复利用. 内存回收机制按照实现角色的不同可以分为两种,一种是程序员手动实现内存的释放(比如C语

随机推荐