jvm——垃圾回收机制(GC)详解

开始之前有几个GC的基本问题

  • 什么是GC?

    GC 是 garbage collection 的缩写,意思是垃圾回收——把内存(特别是堆内存)中不再使用的空间释放掉;清理不再使用的对象。

  • 为什么要GC?

    堆内存是各个线程共享的空间,不能无节制的使用。服务器运行的时间通常都很长。累积的对象也会非常多。这些对象如果不做任何清理,任由它们数量不断累加,内存很快就会耗尽。所以GC就是要把不使用的对象都清理掉,把内存空间空出来,让项目可以持续运行下去。

  • 什么样的对象是垃圾对象?

    不再使用或获取不到的对象是垃圾对象。

  • 如何把垃圾对象找出来?

    办法1:引用计数法(不采用,不能解决循环引用问题)

    办法2:可达性分析(从GC Roots对象出发,不可达的对象就是要清理的对象)

  • 找到垃圾对象如何执行清理?

    具体的GC算法

为什么要有垃圾回收?

  • 线程私有空间:无需由系统来执行GC。因为线程结束,释放自己刚才使用的空间即可,不影响其它线程。
  • 线程共享空间:任何一个线程结束时,都无法确定刚才使用的空间是不是还有别的线程在使用。所以不能因为线程结束而释放空间,必须在系统层面统一垃圾回收。

GC的基本原则:

  • 频繁收集新生代
  • 较少收集老年代
  • 基本不动元空间

如何确定垃圾

想要回收垃圾,必须得先知道,哪些对象可以被认定为垃圾。关于垃圾确定方式,主要有两种,分别是引用计数法可达性分析法,其原理分别如下:

引用计数法(不采用)

工作机制

  • 引用计数法是在对象每一次被引用时,都给这个对象专属的『引用计数器』+1。
  • 当前引用被取消时,就给这个『引用计数器』-1。
  • 当前『引用计数器』为零时,表示这个对象不再被引用了,需要让GC回收。
  • 可是当对象之间存在交叉引用的时候,对象即使处于应该被回收的状态,也没法让『引用计数器』归零。
        Member member01 = new Member();Member member02 = new Member();

images

        member01.setFriend(member02);member02.setFriend(member01);

images

        member01 = null;member02 = null;

images

引用计数法的关键问题:该清理的对象清理不掉

简单的说就是,在 Java 中,引用与对象相关联,如果要操作对象,则必须使用引用。因此,可以通过引用计数来确定对象是否可以回收。实现原则是,如果一个对象被引用一次,计数器 +1,反之亦然。当计数器为 0 时,该对象不被引用,则该对象被视为垃圾,并且可以被 GC 回收利用。

SpringMVC 组件

  • IOC 容器对象的接口类型:WebApplicationContext
    • WebApplicationContext 对象初始化过程中:将它自己存入 ServletContext 域
    • WebApplicationContext 对象也会把 ServletContext 存入 IOC 容器
  • Servlet 上下文对象:ServletContext

可达性分析法

核心原理:判断一个对象,是否存在从『堆外』到『堆内』的引用。

为了解决引用计数法的循环引用问题,Java 采用了可达性分析的方法。其实现原理是,将一系列"GCroot"对象作为搜索起点。如果在"GCroot"和一个对象之间没有可达的路径,则该对象被认为是不可访问的。

要注意的是,不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要经过两次标记过程。两次标记后仍然是可回收对象,则将面临回收。

垃圾回收算法

了解了垃圾的确定方法后,我们将继续了解垃圾是怎么被回收的,即垃圾回收算法。在Java中主要有四中垃圾回收算法,分别是标记清除算法复制算法标记整理算法分代收集算法

基本算法:引用计数法(不推荐)

引用计数算法很简单,它实际上是通过在对象头中分配一个空间来保存该对象被引用的次数。如果该对象被其它对象引用,则它的引用计数加一,如果删除对该对象的引用,那么它的引用计数就减一,当该对象的引用计数为0时,那么该对象就会被回收。

引用计数垃圾收集机制,它只是在引用计数变化为0时即刻发生,而且只针对某一个对象以及它所依赖的其它对象。所以,我们一般也称呼引用计数垃圾收集为直接的垃圾收集机制。垃圾收集的开销被分摊到整个应用程序的运行当中了,而不是在进行垃圾收集时,要挂起整个应用的运行,直到对堆中所有对象的处理都结束。因此,采用引用计数的垃圾收集不属于严格意义上的"Stop-The-World"的垃圾收集机制。

优点:

  • 实时性较高,不需要等到内存不够时才回收
  • 垃圾回收时不用挂起整个程序,不影响程序正常运行

缺点:

  • 回收时不移动对象, 所以会造成内存碎片问题
  • 不能解决对象间的循环引用问题

标记清除算法

“标记-清除”算法是最基础的算法,它的做法是当堆中的有效内存空间被耗尽的时候,就会暂停、挂起整个程序(也被称为stop the world),然后进行两项工作,第一项则是标记,第二项则是清除。

  • 标记:标记的过程其实就是,从根对象开始遍历所有的对象,然后将所有存活的对象标记为可达的对象。

  • 清除:清除的过程将遍历堆中所有的对象,将没有标记的对象全部清除掉。

  • 优点:实现简单

  • 缺点:

    • 效率低,因为标记和清除两个动作都要遍历所有的对象
    • 垃圾收集后有可能会造成大量的内存碎片
    • 垃圾回收时会造成应用程序暂停

标记清除算法.png

由标记清除算法的实现我们可以看出,其主要存在两个缺点:

  • 效率问题。标记和清除过程的效率都不高
  • 空间问题。标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致当程序在以后的运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作

 复制算法

为了解决标记清除算法内存碎片化严重的缺陷,提出了复制算法。复制算法主要思想是,按内存容量将内存划分为大小相等的两块区域。每次只使用其中一块,当这一块内存满后将其中存活的对象复制到另一块上去,然后把该内存中的垃圾对象清理掉,其实现过程如图:

复制算法.png

  • 优点1:在垃圾多的情况下(新生代),效率较高

  • 优点2:清理后,内存无碎片

  • 缺点:浪费了一半的内存空间,在存活对象较多的情况下(老年代),效率较差

复制算法虽然实现简单,内存效率高,不易产生碎片,但是最大的问题是可用内存被压缩到了原本的一半。且存活对象增多的话,Copying 算法的效率会大大降低

标记整理算法

结合了以上两个算法,为了避免缺陷而提出。标记阶段和标记清理算法相同,标记后不是清理对象,而是将存活对象移向内存的一端。然后清除端边界外的对象。如图:

标记整理算法.png

分代收集算法

在结合以上三种算法的综合分析及 JVM 内存对象生命周期的特点,诞生了一种新的垃圾回收算法——分代收集算法。其核心思想是根据对象存活的不同生命周期将内存划分为不同的域,一般情况下将 GC 堆划分为老年代(Tenured/Old Generation)和新生代(Young Generation)。老年代的特点是每次垃圾回收时只有少量对象需要被回收,新生代的特点是每次垃圾回收时都有大量垃圾需要被回收,因此可以根据不同区域选择不同的算法。

分代算法其实就是这样的,根据回收对象的特点进行选择。

  • 新生代适合使用复制算法
  • 老年代适合使用标记清除或标记压缩算法

垃圾回收器

串行垃圾回收器

串行:在一个线程内执行垃圾回收操作。

  • 新生代串行回收器 SerialGC:采用复制算法实现,单线程垃圾回收,独占式垃圾回收器
  • 老年代串行回收器 SerialOldGC:采用标记压缩算法,单线程独占式垃圾回收器

并行垃圾回收器

并行:在多个线程中执行垃圾回收操作。

  • 新生代 ParNew 回收器:采用复制算法实现,多线程回收器,独占式垃圾回收器。
  • 新生代 ParallelScavengeGC 回收器:采用复制算法多线程独占式回收器
  • 老年代 ParallelOldGC 回收器: 采用标记压缩算法,多线程独占式回收器
  1. CMS回收器

    CMS全称 (Concurrent Mark Sweep),是一款并发的、使用标记-清除算法的垃圾回收器。对CPU资源非常敏感。

    启用CMS回收器参数 :-XX:+UseConcMarkSweepGC。

    使用场景:GC过程短暂停顿,适合对时延要求较高的服务,用户线程不允许长时间的停顿。

    优点:最短回收停顿时间为目标的收集器。并发收集,低停顿。

    缺点:服务长时间运行,造成严重的内存碎片化。算法实现比较复杂。

  • G1回收器

    G1(Garbage-First)是一款面向服务端应用的并发垃圾回收器, 主要目标用于配备多颗CPU的服务器,治理大内存。是JDK1.7提供的一个新收集器,是当今收集器技术发展的最前沿成果之一。

    G1计划是并发标记-清除收集器的长期替代品。

    启用G1收集器参数:-XX:+UseG1GC启用G1收集器。

    G1将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了, 它们都是一部分Region(不需要连续)的集合。

    images

    每块区域既有可能属于Old区、也有可能是Young区,因此不需要一次就对整个老年代/新生代回收。而是当线程并发寻找可回收的对象时,有些区块包含可回收的对象要比其他区块多很多。虽然在清理这些区块时G1仍然需要暂停应用线程,但可以用相对较少的时间优先回收垃圾较多的Region(这也是G1命名的来源)。这种方式保证了G1可以在有限的时间内获取尽可能高的收集效率。

    特点:

    • 一整块堆内存被分成多个独立的区域Regions

    • 存活对象被拷贝到新的Survivor区

    • 新生代内存由一组不连续的堆heap区组成,使得可以动态调整各个区域

    • 多线程并发GC

    • young GC会有STW(Stop the world)事件

垃圾回收器对比

新生代回收器

名称串行/并行/并发回收算法适用场景可以与CMS配合
SerialGC串行复制单CPU
ParNewGC并行复制多CPU
ParallelScavengeGC并行复制多CPU且关注吞吐量

老年代回收器

名称串行/并行/并发回收算法适用场景
SerialOldGC串行标记压缩单CPU
ParNewOldGC并行标记压缩多CPU
CMS并发,几乎不会暂停用户线程标记清除多CPU且与用户线程共存

finalize 机制

总体机制介绍

java.lang.Object 类中有一个方法:

protected void finalize() throws Throwable { }

方法体内是空的,说明如果子类不重写这个方法,那么不执行任何逻辑。

  • 在执行 GC 操作前,调用 finalize() 方法的是 Finalizer 线程,这个线程优先级很低。
  • 在对象的整个生命周期过程中,finalize() 方法只会被调用一次。

代码验证

public class FinalizeTest {// 静态变量public static FinalizeTest testObj;@Overrideprotected void finalize() throws Throwable {// 重写 finalize() 方法System.out.println(Thread.currentThread().getName() + " is working");// 给待回收的对象(this)重新建立引用testObj = this;}public static void main(String[] args) {// 1、创建 FinalizeTest 对象FinalizeTest testObj = new FinalizeTest();// 2、取消引用testObj = null;// 3、执行 GC 操作System.gc();// ※ 让主线程等待一会儿,以便调用 finalize() 的线程能够执行try { TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {}// 4、判断待回收的对象是否存在if (FinalizeTest.testObj == null) {System.out.println("待回收的对象没有获救,还是要被 GC 清理");} else {System.out.println("待回收的对象被成功解救");}// 5、再次取消引用FinalizeTest.testObj = null;// 6、再次执行 GC 操作System.gc();// 7、判断待回收的对象是否存在if (FinalizeTest.testObj == null) {System.out.println("待回收的对象没有获救,还是要被 GC 清理");} else {System.out.println("待回收的对象被成功解救");}}}

执行效果:

Finalizer is working
待回收的对象被成功解救
待回收的对象没有获救,还是要被 GC 清理

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/92650.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

flutter开发实战-just_audio实现播放音频暂停音频设置音量等

flutter开发实战-just_audio实现播放音频暂停音频设置音量等 最近开发过程中遇到需要播放背景音等音频播放,这里使用just_audio来实现播放音频暂停音频设置音量等 一、引入just_audio 在pubspec.yaml引入just_audio just_audio: ^2.7.0在iOS上,video_p…

CI/CD流水线实战

不知道为什么,现在什么技术都想学,因为我觉得我遇到了技术的壁垒,大的项目接触不到,做的项目一个字辣*。所以,整个人心浮气躁,我已经得通过每天的骑行和长跑缓解这种浮躁了。一个周末,我再次宅在…

Docker容器基础

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、Docker概述1、docker是什么2、Docker的设计宗旨3、容器在内核中支持2种重要技术: 三、Docker的核心概念四、Docker相关命令1.安装依赖包2.设置阿里云…

STM32 F103C8T6学习笔记2:GPIO的认识—GPIO的基本输入输出—点亮一个LED

今日继续学习使用 STM32 F103C8T6开发板 点亮一个LED灯,文章提供源码,测试工程,实验效果图,希望我的归纳总结会对大家有帮助~ 目录 GPIO的认识与分类 : 引脚安排整理: 定时器的引脚例举: …

Linux:shell脚本 正则表达式与AWK

一、正则表达式 由一类特殊字符及文本字符所编写的模式,其中有些字符(元字符)不表示字符字面意义,而表示控制或通配的功能,类似于增强版的通配符功能,但与通配符不同,通配符功能是用来处理文件…

硬件产品经理:从入门到精通(新书发布)

目录 简介 新书 框架内容 相关课程 简介 在完成多款硬件产品从设计到推向市场的过程后。 笔者于2020年开始在产品领域平台输出硬件相关的内容。 在这个过程中经常会收到很多读者的留言,希望能推荐一些硬件相关的书籍或资料。 其实,笔者刚开始做硬…

【UE】Web Browser内嵌网页的使用

零、准备 1.在Edit菜单打开插件界面 搜索Web Browser并勾选,按提示重启引擎。 2.在资源窗口右键创建Widget Blueprint,并打开 3.搜索canvas panel 并拖拽到下方 4.在实验分类中找到Web Browser拖拽到Canvs Panel下 4.选中WebBrowser在右侧细节面板中…

pycharm,VSCode 几个好用的插件

pycharm Tabnine AI Code 可以在编写程序的时候为你提供一些快捷方式,增加编程速度 Chinese 对英文不好的程序员来说是个不错的选择,可以将英文状态下的pycharm变为中文版的 ChatGPT 可以跟ai聊天,ai可以解决你80%的问题 ,也可以帮…

用对角线去遍历矩阵

声明 该系列文章仅仅展示个人的解题思路和分析过程,并非一定是优质题解,重要的是通过分析和解决问题能让我们逐渐熟练和成长,从新手到大佬离不开一个磨练的过程,加油! 原题链接 用对角线遍历矩阵https://leetcode.c…

自动化安装系统(一)

系统安装过程 加载boot loader加载启动安装菜单加载内核和initrd文件加载根系统运行anaconda的安装向导 安装光盘中与安装相关的文件 安装autofs启动后会自动出现/misc目录。 在虚拟机设置中添加CD/DVD,使用系统ISO文件,登录系统后mount /dev/cdrom …

【UE4 RTS】07-Camera Boundaries

前言 本篇实现的效果是当CameraPawn移动到地图边缘时会被阻挡。 效果 步骤 1. 打开项目设置,在“引擎-碰撞”中,点击“新建Object通道” 新建通道命名为“MapBoundaries”,然后点击接受 2. 向视口中添加 阻挡体积 调整阻挡体积的缩放 向四…

Redis中的数据类型

Redis中的数据类型 Redis存储的是key-value结构的数据,其中key是字符串类型,value有5种常用的数据类型: 字符串string哈希hash列表list集合set有序集合sorted set

【vue】简洁优雅的火花线、趋势线

来由 在github发现个好看易用的vue趋势线组件,特此记录。 效果 趋势图生成后效果如上,线条为渐变色,可设置是否平滑。具体线条走势,根据数据动态生成。 使用 安装 npm i vuetrend -S 引入 import Vue from "vue"…

Triangle Area

三角形面积,同底等高,等底等高,等底同高,转换 2 3 5 7 10 15 ? 32

Python系统学习1-9-类(一)

一、类之初印象 1、类就是空表格,将变量(列名)和函数(行为)结合起来 2、创建对象,表达具体行 3、创建类就是创建数据的模板 --操作数据时有提示 --还能再组合数据的行为 --结构更加清晰 4、类的内存分配…

LeetCode 778. Swim in Rising Water【最小瓶颈路;二分+BFS或DFS;计数排序+并查集;最小生成树】2096

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章…

计算机视觉五大核心研究任务全解:分类识别、检测分割、人体分析、三维视觉、视频分析

目录 一、引言1.1 计算机视觉的定义1.1.1 核心技术1.1.2 应用场景 1.2 历史背景及发展1.2.1 1960s-1980s: 初期阶段1.2.2 1990s-2000s: 机器学习时代1.2.3 2010s-现在: 深度学习的革命 1.3 应用领域概览1.3.1 工业自动化1.3.2 医疗图像分析1.3.3 自动驾驶1.3.4 虚拟现实与增强现…

java面试基础 -- ArrayList 和 LinkedList有什么区别

目录 基本介绍 有什么不同?? ArrayList的扩容机制 ArrayLIst的基本使用 基本介绍 还记得我们的java集合框架吗, 我们来复习一下, 如图: 可以看出来 ArrayList和LinkedList 都是具体类, 他们都是接口List的实现类. 但是他们底层的逻辑是不同的, 相信学过这个的应该大概有…

CI+JUnit5并发单测机制创新实践

目录 一. 现状问题 二. 分析原因 三. 采取措施 四. 实践步骤 五. 效能提升 资料获取方法 一. 现状问题 针对现如今高并发场景的业务系统,“并发问题” 终归是必不可少的一类(占比接近10%),每次出现问题和事故后&#xff0c…

JVM基础了解

JVM 是java虚拟机。 作用:运行并管理java源码文件锁生成的Class文件;在不同的操作系统上安装不同的JVM,从而实现了跨平台的保证。一般在安装完JDK或者JRE之后,其中就已经内置了JVM,只需要将Class文件交给JVM即可 写好的…