首页 > 编程语言 > Java 垃圾回收机制

Java 垃圾回收机制

时间:2022-10-20 23:01:02浏览次数:55  
标签:Java 收集器 对象 标记 回收 GC 垃圾

一、垃圾回收之标记算法

 1. 引用计数法

  • 通过判断对象的引用数量来决定对象是否被回收
  • 每个对象实例都有一个引用计数器,被引用则+1,完成引用则-1

  优点: 执行效率高,程序执行受影响小

  缺点: 无法检测出循环引用的情况,导致内存泄漏

 应用较少

 2. 可达性分析算法

  通过判断对象的引用链是否可达来决定对象是否可以被回收。

  将程序中所有的引用链视为一张图,从一系列的GC Root 出发,无法到达的节点则标记为可回收。如下图所示:

  可以作为GC Root的对象:

  • 虚拟机栈中引用的对象(栈帧中的局部变量表)
  • 方法区中的常量引用的对象
  • 方法区中的类静态属性引用的对象
  • 本地方法栈中JNI的引用对象
  • 活跃线程的引用对象

 二、垃圾回收之回收算法

1. 标记清除算法

  • 标记:从根集合进行扫描,对存活对象进行标记
  • 清除:对堆内存从头到尾进行线性遍历,回收不可达对象内存

  缺点:碎片化严重

2. 复制算法

  将管理的内存块分为多个块区,在其中一部分块区中创建对象分配内存(我们称之为对象面)。当对象面内存用完时,将存活的对象复制到没有使用的那些块区(称之为空闲面),再将对象面的所有对象内存清除。适用于对象存活率较低的场景,比如新生代。

  • 解决了碎片化问题
  • 顺序分配内存,简单高效
  • 适用于对象存活率低的场景

  

3. 标记整理算法

  1) 标记: 从根集合进行扫描,对存活对象进行标记

  2)整理:移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收

特点:

  • 避免了内存的不连续性
  • 不用设置两块内存互换
  • 适用于存活率高的场景,例如 老年代

4. 分代收集算法

  分代整理算法可以看作是复制算法和标记整理算法的结合。将内存区域划分为年轻代和老年代,自然就有各自的回收方法。内存分区示意如下:

  GC的分类:

  • Minor GC:年轻代的垃圾收集,采用复制算法。执行较为频繁,尽可能快速地回收生命周期短的对象。
  • Full GC/ Major GC: 老年代回收,一般情况下同时也会触发Minor GC

  

4.1 年轻代

  年轻代划分为 Eden区和两个servivor区。新创建的对象 内存首选分配在Eden区,当Eden区分配不下时,可能会分配在Survivor区或直接分配在老年代。

  当Eden区内存用完,需要回收时,使用复制算法,将Eden区和servivor的from区一起复制到servivor-to区(两个servivor区没有区别,哪个有对象分配就是from,空的就是to)。如此循环往复,对象每存活一次,年龄就加1。默认年龄为15岁,复制到老年代,可以通过 -XX:MaxTenuringThreshold 参数修改。

  对象如何晋升到老年代:

  • 经历一定Minor次数依然存活的对象,默认15次。
  • Servivor区存放不下的对象
  • 新生成的大对象 (可通过  -XX: PretenuerSizeThreshold参数配置大小,超过及分配至老年代)

 常用的调优参数:

  • -XX:SurvivorRatio:   Eden和Survivor的比值,默认8:1
  • -XX:NewRatio:     老年代和新生代内存比例,默认2:1
  • -XX:MaxTenuringThreshold:    新生代进入老年代的年龄值, 默认15
  • -XX:  PretenuerSizeThreshold: 多大的对象直接分配在老年代
  • -Xms:初始化堆内存大小
  • -Xmx:最大堆内存大小
  • -Xss:每个线程栈空间大小

4.2 老年代

  老年代: 存放生命周期较长的对象。回收采用 标记-清除 或标记-整理算法,一般也会触发Minor GC,所以我们称其为 Full GC。

  Full GC比MinorGC慢,执行效率低。 执行频率也低。

  触发Full GC的条件:

  • 老年代空间不足
  • CMS GC时 出现promotion failed, concurrent mode failure
  • Minor GC 晋升到老年代的平均大小大于老年代的剩余空间
  • 调用 System.gc(),通知JVM回收,但JVM不一定会执行

三、常见的垃圾回收器 

 1. 年轻代垃圾回收器

  Serial收集器(-XX:UseSerialGC, 复制算法)

  • 单线程收集,进行垃圾收集时,必须暂停所有工作线程
  • 简单高效,Client模式下默认的年轻代收集器

  ParNew收集器(-XX:UseParNewGC, 复制算法)

  • 多线程收集,其余的行为特点和Serial收集器一样
  • 单核执行效率不如Serial,在多核下执行才有优势 

  Parallel Scavenge收集器 (-XX:UseParallelGC, 复制算法)

  • 比起关注用户线程停顿时间,更关注系统的吞吐量(这里吞吐量是指  运行用户代码时间/(运行用户代码时间+垃圾收集时间))
  • 在多核下执行有优势,Server模式下默认的年轻代收集器

 2.老年代垃圾回收器

  Serial Old收集器(-XX:UseSerialOldGC, 标记-整理算法)

  • 单线程收集,进行垃圾收集时,必须暂停所有工作线程
  • 简单高效,Client模式下默认的老年代收集器

  Parallel Old收集器(-XX:UseParallelOldGC,标记-整理算法)

  • 多线程, 吞吐量优先

  CMS收集器(-XX:UseConcMarkSweepGC, 标记清除算法)

  CMS全称 Concurrent Mark Sweep,是一款并发的、使用标记-清除算法的垃圾回收器,以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器,对于要求服务器响应速度的应用上,这种垃圾回收器非常适合。

  回收流程:

  • 初始标记:在这个阶段,需要虚拟机停顿正在执行的任务,官方的叫法STW(Stop The Word)。这个过程从垃圾回收的"根对象"开始,只扫描到能够和"根对象"直接关联的对象,并作标记。所以这个过程虽然暂停了整个JVM,但是很快就完成了。
  • 并发标记:这个阶段紧随初始标记阶段,在初始标记的基础上继续向下追溯标记。并发标记阶段,应用程序的线程和并发标记的线程并发执行,所以用户不会感受到停顿。
  • 并发预清理:并发预清理阶段仍然是并发的。在这个阶段,虚拟机查找在执行并发标记阶段新进入老年代的对象(可能会有一些对象从新生代晋升到老年代, 或者有一些对象被分配到老年代)。通过重新扫描,减少下一个阶段"重新标记"的工作,因为下一个阶段会Stop The World。
  • 重新标记:这个阶段会stop the world,收集器线程扫描在CMS堆中剩余的对象。扫描从"根对象"开始向下追溯,并处理对象关联。
  • 并发清理:清理垃圾对象,收集器线程和应用程序线程并发执行
  • 并发重置:这个阶段,重置CMS收集器的数据结构,等待下一次垃圾回收。

  CMS不会整理、压缩堆空间,这样就带来一个问题:经过CMS收集的堆会产生空间碎片,CMS不对堆空间整理压缩节约了垃圾回收的停顿时间,但也带来的堆空间的浪费。为了解决堆空间浪费问题,CMS回收器不再采用简单的指针指向一块可用堆空 间来为下次对象分配使用。;而是把一些未分配的空间汇总成一个列表,当JVM分配对象空间的时候,会搜索这个列表找到足够大的空间来hold住这个对象。

  3. G1 收集器(-XX: UseG1GC, 复制+ 标记整理算法,独立的垃圾回收器包含新生代、老年代)

  G1(Garbage-First)是一款面向服务端应用的垃圾收集器,主要针对配备多核CPU及大容量内存的机器,以极高概率满足GC停顿时间的同时,还兼具高吞吐量的性能特征。JDK 9以后的默认垃圾收集器,取代了CMS 回收器。

 4. GC关系图:连线代表可以搭配使用

四、Java引用类型

 1. 强引用(Strong Reference)

  • 最普遍的引用: Object obj = new Object()
  • 抛出OutOfMemoryError终止程序也不会回收具有强引用的对象

 2. 软引用 (Soft Reference)

  • 对象处在有用但非必须的状态
  • 只有当空间不足时,GC会回收该引用的对象的内存
  • 可以用来实现高速缓存

  使用方法: 

String str = new String("abc");
SoftReference<String> softRef = new SoftReference<String>(str);

 3.弱引用(Weak Reference)

  • 非必须的对象,比软引用更弱一些
  • GC时会被回收
  • 适用于偶尔被使用且不影响垃圾收集的对象

  使用方法:

String str = new String("abc");
WeakReference<String> weakRef = new WeakReference<String>(str);

 4.虚引用 (PhantomReference)

  • 不会决定对象的生命周期
  • 任何时候都可能被垃圾收集器回收
  • 跟踪对象被垃圾收集器回收的活动,起哨兵作用
  • 必须和引用队列ReferenceQueue联合使用 
String str = new String("abc");
ReferenceQueue rq = new ReferenceQueue();
PhantomReference ref = new PhantomReference(str, queue);

  

引用类型   被垃圾回收时间   用途   生存时间
强引用 从来不会 对象的一般状态 JVM停止运行时终止
软引用 在内存不足时 对象缓存 内存不足时终止
弱引用 在垃圾回收时 对象缓存 GC运行后终止
虚引用 Unknown 标记、哨兵 Unknown

 

 

标签:Java,收集器,对象,标记,回收,GC,垃圾
From: https://www.cnblogs.com/lostO/p/16807270.html

相关文章

  • JavaScript中的Promise
    阮一峰ES6入门Promise1.Promise的介绍Promise是异步编程的一种新的解决方案,从早期的回调函数、事件相比,更加合理和强大。语法上来说,Promise是一个对象,可以获取异......
  • 多测师肖sir__web__java自动化__DOM
    DOM一、DOM作用通过可编程的对象模型,javascript 获得了足够的能力来改变html1、改变页面html元素2、改变页面html属性3、改变页面css样式,对页面中的所有事件做出反......
  • 多测师肖sir__web__java自动化__selenium IDE
    SeleniumIDESeleniumIDE是一个Firefox/Chrome插件,用于记录和回放用户与浏览器的交互。使用它来创建简单的脚本或协助进行探索性测试。SeleniumIDE安装: 官网下载插......
  • java和unicode
    java中忘记了的基础知识:   在jvm中,java中的字符(char)保存的是对应字符的unicode码。  例如‘中’字的unicode码是20013,16进制是\u4e2d,代码publicstatic......
  • 今天说下怎么创建Java后端框架Spring Boot的项目
    使用java编程,就不得不提到久负盛名的Java后端框架SpringBoot今天来跟大家说下怎么创建SpringBoot的第一个项目,都需要做哪些准备吧。首先介绍下SpringBoot主要提供了......
  • Java基础(九)| ArrayList详解与应用
    ⭐本专栏旨在对JAVA的基础语法及知识点进行全面且详细的讲解,完成从0到1的java学习,面向零基础及入门的学习者,通过专栏的学习可以熟练掌握JAVA编程,同时为后续的框架学习,进阶开......
  • 在Java中需要特殊处理时间的方法
    在开发中会遇到时间的特殊处理例如:时间转换,未来时间(当前时间加上分时天),时间相差1.时间转换有时候时间格式不对,不是我们需要的,那就需要转换,转换成这样的2022-1......
  • 【java json基础】字符串转json json转字符串 json数组转换 【java基础知识】【实用】
    读书就是:从薄读厚,再从厚读薄的过程。(前部分:问答,后部分:测试举例)         数据测试:举例:      ......
  • Java四种线程池
    1,线程池的作用 线程池作用就是限制系统中执行线程的数量。 根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果。 2,为什么要用线程池? (1).减少了创建......
  • java---LocalDateTime
    packagejavasm.demo02;importjava.time.*;importjava.time.temporal.ChronoUnit;publicclassLocalDaTime{publicstaticvoidmain(String[]args){Loc......