java进阶3:GC 的背景与一般原理

news2024/11/24 3:20:51

目录

为什么会有GC

背景:

理解:

可达性分析算法

基本思路:

标记清除算法

​讲解

一 点睛

二 执行过程

三 什么是清除

四 缺点

STW

对象与分代

GC时对象在内存池中的迁移

GC Roots

总结

默认算法

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

复制算法(Copying)

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

视频


为什么会有GC

其最本质的原因是因为内存资源的稀缺性。我们计算机最核心的资源是CPU和内存,CPU是随着计算机一直存在的东西,核数有限但是一直存在;但内存比较稀缺,A占满了,B就不能用了,我们怎么可以共享使用这个内存呢,这就是GC产生的原因了。

背景:

在传统的编程语言中,程序员需要手动分配和释放内存空间。这样的内存管理方式容易出现内存泄漏(Memory Leak)和空指针异常(NullPointerException)等问题,同时也增加了编程的复杂性和难度。

为了解决这些问题,Java引入了垃圾回收机制。垃圾回收器负责自动检测和释放不再使用的对象,将它们占用的内存空间返还给系统。这样,程序员无需关心对象的生命周期,也不需要手动释放内存,大大简化了程序的开发和维护工作。

Java中的GC背景还包括以下几个方面:

1.动态内存分配:Java中的对象都是在堆(Heap)上分配内存的,而非栈(Stack)上。堆是动态分配和回收的,允许程序在运行时创建和销毁对象,不需要静态定义和预先分配内存空间。

2.分代垃圾回收:Java的垃圾回收机制根据对象的生命周期进行不同的处理。一般来说,新创建的对象往往生命周期较短,而已经存活一段时间的对象可能会存活更长时间。为了提高垃圾回收的效率,Java将内存分为不同的代(Generation),并采用不同的回收策略来处理。

3.垃圾回收算法:Java的垃圾回收机制采用的是可达性分析算法。该算法通过判断对象是否可达(即是否可以通过根对象(如局部变量、类的静态变量等)追溯到)来确定其是否存活。如果对象不可达,则被认为是垃圾,可以被回收。

总之,Java中的垃圾回收机制背后有着动态内存分配、分代垃圾回收和可达性分析等背景。它大大简化了程序员对内存管理的工作,提高了程序的可靠性和开发效率。

理解:

GC是垃圾收集的意思(Garbage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操作方法。

所以,Java的内存管理实际上就是对象的管理,其中包括对象的分配和释放。

对于Java程序员来说,分配对象使用new关键字;释放对象时,只要将对象所有引用赋值为null,让程序不能够再访问到这个对象,我们称该对象为"不可达的".GC将负责回收所有"不可达"对象的内存空间。

对于GC来说,当程序员创建对象时,GC就开始监控这个对象的地址、大小以及使用情况。通常,GC采用有向图的方式记录和管理堆(heap)中的所有对象。通过这种方式确定哪些对象是"可达的",哪些对象是"不可达的".当GC确定一些对象为"不可达"时,GC就有责任回收这些内存空间。但是,为了保证GC能够在不同平台实现的问题,Java规范对GC的很多行为都没有进行严格的规定。例如,对于采用什么类型的回收算法、什么时候进行回收等重要问题都没有明确的规定。因此,不同的JVM的实现者往往有不同的实现算法。这也给Java程序员的开发带来许多不确定性。本文研究了几个与GC工作相关的问题,努力减少这种不确定性给Java程序带来的负面影响。

可达性分析算法

引用链法(可达性分析法)

基本思路:

可达性分析算法是以根对象集合(GC Roots)为起始点,按照从上到下的⽅式搜索被根对象集合所连接的⽬标对象是否可达;

使⽤可达性分析算法后,内存中的存活对象都会被根对象集合直接或间接连接着,搜索所⾛过的路径称为引⽤链(ReferenceChain);

如果⽬标对象没有任何引⽤链相连,则是不可达的,就意味着该对象已经死亡,可以标记为垃圾对象;

在可达性分析算法中,只有能够被根对象集合直接或间接连接的对象才是存活对象。

标记清除算法

引用计数法在JVM垃圾回收算法中逐渐被废弃,很简单,如果存在对象之间的循环引用,则计数器的count值永远不会清0,如此对象将会一直存在内存中得不到释放。最后就导致我们从内存泄漏到内存溢出。

优化:引用计数->引用追踪(标记清除算法)

​讲解

一 点睛

当成功区分出内存中存活对象和死亡对象后,GC 接下来的任务就是执行垃圾回收,释放掉无用对象所占用的内存空间,以便有足够的可用内存空间为新对象分配内存。

标记-清除算法(Mark-Sweep)是一种非常基础和常见的垃圾收集算法,该算法被 J.McCarthy 等人在1960年提出并并应用于Lisp语言。

二 执行过程

当堆中的有效内存空间(available memory)被耗尽的时候,就会停止整个程序(也被称为stop the world),然后进行两项工作,第一项则是标记,第二项则是清除。

标记:Collector 从引用根节点开始遍历,标记所有被引用的对象。一般是在对象的 Header 中记录为可达对象。标记的是引用的对象,不是垃圾!!

清除:Collector 对堆内存从头到尾进行线性的遍历,如果发现某个对象在其 Header中 没有标记为可达对象,则将其回收

三 什么是清除

这里所谓的清除并不是真的置空,而是把需要清除的对象地址保存在空闲的地址列表里。下次有新对象需要加载时,判断垃圾的位置空间是否够,如果够,就存放覆盖原有的位置。

如果内存规整

- 采用指针碰撞的方式进行内存分配

如果内存不规整

- 虚拟机需要维护一个列表

- 空闲列表分配

四 缺点

标记清除算法的效率不算高。

在进行GC的时候,需要停止整个应用程序,用户体验较差。

在GC标记-清除算法的使用过程中会逐渐产生被细化的分块,不久后就会导致无数的小分块散布在堆的各处。我们称这种状况为碎片化(fragmentation)。

STW

Stop-the-World,简称STW。

暂停原因:STW的主要目的是为了在垃圾回收期间保证应用程序的一致性。当Java虚拟机启动垃圾回收时,它会暂停所有的应用线程,以防止在垃圾回收期间发生数据竞争或并发错误。

1、指的是GC事件发生过程中,会产生应用程序的停顿。停顿产生时整个应用程序线程都会被暂停,没有任何响应, 有点像卡死的感觉,这个停顿称为STW。

(1)可达性分析算法中枚举根节点(GC Roots)会导致所有Java执行线程停顿。 ① 分析工作必须在一一个能确保一 致性的快照中进行 ② 一致性指整个分析期间整个执行系统看起来像被冻结在某个时间点上 ③ 如果出现分析过程中对象引用关系还在不断变化,则分析结果的准确性无法保证

(2)被STW中断的应用程序线程会在完成GC之后恢复,频繁中断会让用户感觉像是网速不快造成电影卡带一样, 所以我们需要减少STW的发生。

2、STW事件和采用哪款GC无关,所有的GC都有这个事件。

3、哪怕是G1也不能完全避免stop-the-world情况发生,只能说垃圾回收器越来越优秀,回收效率越来越高,尽可能地缩短了暂停时间。

4、STW是JVM在后台自动发起和自动完成的。在用户不可见的情况下,把用户正常的工作线程全部停掉。开发中不要用System.gc() ;会导致stop-the-world的发生。

减少系统的停顿时间(STW),这个是我们后面优化算法统一的核心目标。

注意:这里压缩不是说的标记清除算法而是以后的其它算法。比如标记-压缩算法(Mark-Compact Algorithm)和标记-整理算法(Mark-Sweep-Compact Algorithm)等。

对象与分代

java中的对象分代是一种内存管理策略,它将堆内存中的对象划分为不同的代(Generation),并针对不同代的对象使用不同的垃圾回收算法和策略。Java虚拟机通常将堆内存分为年轻代(Young Generation)、老年代(Old Generation)和永久代(Permanent Generation 或者说是Metaspace)。

  1. 年轻代(Young Generation):年轻代是新创建的对象的分配区域,通常包含三个区域:Eden区、Survivor区From和Survivor区To。新创建的对象首先分配在Eden区,当Eden区满时,会触发Minor GC(Minor Garbage Collection)。Minor GC的目标是清理年轻代的垃圾对象,并将存活的对象复制到Survivor区中的一个区域。经过多次Minor GC后,仍然存活的对象会被晋升到老年代。

  2. 老年代(Old Generation):老年代用于存放生命周期较长的对象,包括年轻代晋升的对象和直接在老年代分配的大对象。当老年代的空间不够时,会触发Full GC(Full Garbage Collection),Full GC会遍历整个堆内存,清理全部的垃圾对象。

  3. 永久代(Permanent Generation 或者说是Metaspace):永久代主要用于存放类的元数据(metadata)和常量池(constant pool)等。在Java 8之后,永久代被Metaspace取代,Metaspace使用本地内存进行类的元数据存储。

通过分代的概念,Java虚拟机可以根据对象的生命周期使用不同的垃圾回收算法和策略。例如,年轻代通常使用复制算法进行垃圾回收,每次清理少量的对象,而老年代由于对象生命周期较长且稳定,通常使用标记-整理算法或标记-清除-整理算法进行垃圾回收。

分代的设计目的是为了提高垃圾回收的效率。通常,大部分对象都有较短的生命周期,而只有少部分对象拥有较长的生命周期。通过将对象按照生命周期划分到不同的代中,可以针对不同代使用适当的垃圾回收算法和策略,从而提高垃圾回收的效率和系统的性能。

GC时对象在内存池中的迁移

  1. 发生Minor GC时,首先将Eden区和Survivor区From区域中的存活对象复制到空的Survivor区To区域。

  2. 复制完成后,清空Eden区和Survivor区From区域,将它们作为空闲空间。

  3. 下一次Minor GC时,将Eden区和Survivor区To区域中的存活对象复制到Survivor区From区域或者老年代(取决于对象的年龄)。

  4. 此后,对象会在From区和To区之间进行复制,年龄达到一定阈值(通常是15岁)的对象将会被晋升到老年代。

  5. 新生代Eden区满时,发生Minor GC,通过GC Roots引用链找存活的对象,回收垃圾对象;老年代内存满时,发生Full GC(Major GC),回收老年代的垃圾对象;

  6. 这两种垃圾回收的目标和作用范围是不同的。Full GC(Major GC)会遍历和清理整个堆内存,而Minor GC主要涉及到年轻代的Eden区和Survivor区。

  7. 如果Full GC仍然无法释放足够的内存空间,可能会抛出OutOfMemoryError异常。

为什么是复制,不是移动?

复制算法的优势:复制算法相对于移动操作的主要优势在于简单性和效率。由于复制操作是将存活对象复制到一个空的区域,因此不需要对整个堆内存进行扫描和标记。这样可以减少标记和清除的开销,提高垃圾回收的效率。同时,由于每次垃圾回收后都是对整个区域进行清空,可以保证内存分配的连续性,解决了内存碎片问题。

移动操作的劣势:移动对象需要更新所有对该对象的引用,涉及到对堆内存中所有引用该对象的地方进行更新,这对于大型对象或者引用链较长的情况来说开销较大,并且容易引入安全问题。而复制操作只涉及到存活对象的复制,不需要修改引用的指向,不会涉及到全局的引用更新,因此更加高效且安全。

对象销毁过程

Java对象的销毁指的是释放对象占用的内存空间,JVM通过GC机制实现内存的自动回收

GC Roots

可以作为 GC Roots 的对象 

1.当前正在执行的方法里的局部变量和输入参数

2.活动线程 (Active threads)

3.所有类的静态字段 (static field)

4.JNI 引用

此阶段暂停的时间,与堆内存大小,对象的总数没有直接关系,而是由存活对象 (aliveobiects) 的数量来决定。所以增加堆内存的大小并不会直接影响标记阶段暂停的时间。

总结

默认算法

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

标记---清除算法(Mark-Sweep)是一种非常基础和常见的垃圾收集算法,该算法被J.McCarthy等人在1960年提出并并应用于Lisp语言。标记清除的执行过程是先标记,再清除。

特点:实现简单

缺点:每次清除的时候都需要停机、存在内存空间太强片化问题。

复制算法(Copying)

​复制(Copying)算法是为了解决标记-清除算法,的效率和收集的时间空间不连续等问题。主要的实现是将空间分为两份,将存活的对象移到另外一份,标记完后,将原来的空间清除,这样的话空间是连续的,并且效率较高。

特点:空间连续无碎片化、清除高效;

缺点:

压缩一半空间,垃圾清楚的时候一半空间不可用。

对存活对象较多的老年代下,交率较差。

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

​由于复制算法的高效性是建立在存活对象少,垃圾对象多的前提下的,对于新生代来说比较适合,但是针对老年代来说,很多对象是一直存活的,所以就不能用复制算法,这样会导致每次回收的垃圾很少,会造成大量的复制。所以标记-整理算法主要是针对老年代来设计的。其原理主要是:分为两个阶段,第一个阶段与标记-清理算法一样,先从根节点标记哪些是被对象引用的,第二阶段将所有存活的对象压缩移动到内存的另一端,按顺序排放,最后清除所有边界以外的空间。

注意:在JDK8默认的配置下使用 新生代,老年代的垃圾回收策略,新生代区域使用标记-复制算法,老年代区域使用标记-整理算法。

对比名称

标记-清除

标记-整理

标记-复制

速度

中等

最慢

最快

空间开销

少(会产生碎片)

少(不会产生碎片)

需要对象2倍大小

移动对象

视频

​链接:https://www.aliyundrive.com/s/RTG4PHVxq5w

今天就到这里吧,感觉有用的小伙伴可以点个赞,你的支持就是我更新的最大动力!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenCV创建一张类型为CV_8UC4的4通道图像

#include <iostream> #include <opencv2/imgcodecs.hpp> #include <opencv2/opencv.hpp> #include <opencv2/highgui.hpp>int m

Openlayers实战:自定义版权属性信息

Openlayers地图中,通常会展示地图的一个版权信息,这里面涉及到地图层的版权信息内容,还涉及到control中的Attribution的设置,本实战示例中,通过灵活的属性配置,显示了还是大剑师兰特的博客版权信息,点击是可以跳转的。 效果图 源代码 /* * @Author: 大剑师兰特(xiaoz…

【新版系统架构】第十二章-信息系统架构设计理论和实践

软考-系统架构设计师知识点提炼-系统架构设计师教程&#xff08;第2版&#xff09; 第一章-绪论第二章-计算机系统基础知识&#xff08;一&#xff09;第二章-计算机系统基础知识&#xff08;二&#xff09;第三章-信息系统基础知识第四章-信息安全技术基础知识第五章-软件工程…

2023年SCI影响因子(JCR2022)正式公布

2023年6月28日&#xff0c;Clarivate Analytics&#xff08;科睿唯安&#xff09;发布最新的《期刊引证报告》&#xff08;Journal Citation Reports&#xff0c;简称JCR&#xff09;&#xff0c;刷新SCI期刊2022年影响因子(IF)。该指数也备受访问学者、联培博士及博士后研究者…

Centos7安装编译ffmpeg到指定目录

1、官网下载tar包 https://ffmpeg.org/download.html 2、解压usr/local目录 3、编译准备 cd ffmpeg-6.0 # 切换到ffmpeg-6.0目录 yum install gcc # 安装gcc编译器 yum install yasm # 安装yasm编译器 4、安装设置 ./configure --enable-shared --prefix/usr/local/ffmp…

AI 对抗超级细菌:麦克马斯特大学利用深度学习发现新型抗生素 abaucin

内容一览&#xff1a;鲍曼不动杆菌是一种常见的医院获得性革兰氏阴性病原体&#xff0c;通常表现出多重耐药性。利用传统方法&#xff0c;发现抑制此菌的新型抗生素很困难。但利用机器学习可以快速探索化学空间&#xff0c;从而增加发现新型抗菌分子的可能性。近期&#xff0c;…

基于STM32智能窗帘控制系统仿真设计(含源程序+proteus仿真+讲解视频)

# 基于STM32智能窗帘设计&#xff08;含源程序proteus仿真&#xff09; 仿真&#xff1a;proteus8.11 程序编译器&#xff1a;keil 5 编程语言&#xff1a;C语言 编号C0007 资料下载链接 讲解视频 基于STM32的简易智能窗帘控制系统仿真设计 功能说明&#xff1a; 实现功能&a…

浅谈金融场景的风控策略

随着互联网垂直电商、消费金融等领域的快速崛起&#xff0c;用户及互联网、金融平台受到欺诈的风险也急剧增加。网络黑灰产已形成完整的、成熟的产业链&#xff0c;每年千亿级别的投入规模&#xff0c;超过1000万的“从业者”&#xff0c;其专业度也高于大多数技术人员&#xf…

innovus:earlyGlobalRoute与trial route

在innovus很早的版本里&#xff0c;比如早期encounter&#xff0c;earlyGlobalRoute还叫trial route&#xff0c;所以这二者没有本质区别&#xff0c;使用setRouteMode可以针对early global route的绕线层次等属性进行设置&#xff0c;除了对earlyGlobal Route命令有显性约束外…

小端机器上读取数值、字符串到寄存器的实现

目录 0. 问题 1. 对于从内存读取/写入的数据 2. 对于从常量直接写入寄存器 0. 问题 小端机器上&#xff0c;对于以下两条指令&#xff0c;实现如下&#xff1a; mov eax, 0x78FF5ABC //eax里存的是0x78FF5ABC mov ebx,WXYZ //ebx里存的是0x5a595857&#xff0c;对应于…

Ribbon源码分析(负载均衡源码)

Bean LoadBalanced //ribbon 的负载均衡注解public RestTemplate restTemplate() {return new RestTemplate(); }加了LoadBalanced //ribbon 的负载均衡注解的RestTemplate&#xff0c;是必须要走Ribbon 的流程&#xff08;见4.1&#xff09;的。接上文&#xff0c;如果想用原生…

通过ioctl函数选择不同硬件的控制,LED 蜂鸣器 马达 风扇

通过ioctl函数选择不同硬件的控制&#xff0c;LED 蜂鸣器 马达 风扇 实验现象 head.h #ifndef __HEAD_H__ #define __HEAD_H__ typedef struct{volatile unsigned int MODER; // 0x00volatile unsigned int OTYPER; // 0x04volatile unsigned int OSPEEDR; // 0x08volati…

【数学建模】 MATLAB 蚁群算法

蚁群算法 MATLAB–基于蚁群算法的机器人最短路径规划* https://blog.csdn.net/woai210shiyanshi/article/details/104712540?ops_request_misc%257B%2522request%255Fid%2522%253A%2522168853912916800215023827%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fa…

浅谈电动自行车充电桩智能监测系统的设计与应用

安科瑞 华楠 摘要&#xff1a;围绕基于物联网的电动自行车充电桩智能监测系统&#xff0c;介绍了该系统的软硬件组成、各部分之间的联系&#xff0c;以及该系统的工作原理、相应的设计技术&#xff0c;主要包括电动车充电桩的智能控制方法、充电桩与手机的通信方法&#xff0c…

免费远程控制/局网远控软件

亲测可用&#xff0c;永久免费。适用于局网远控。 一、Quasar 免费开源 二、Solarwinds Dameware Mini Remote Control 收费

小白开酒吧前要知道的几个知识(四)

第七、岗位分工 酒吧一定要分工明确&#xff0c;各司其职。每一个岗位都有着自己的职责&#xff0c;每一个环节都有所关联&#xff0c;每天上班前需要提前安排好各岗位的工作。团队需要一个规章制度&#xff0c;毕竟没有规矩不成方圆&#xff0c;建立岗位相关的工作制度以及责…

linux应用docker基本使用(一)

相关文章&#xff1a; linux docker安装及报错处理_做测试的喵酱的博客-CSDN博客 一、mac 安装及使用docker_docker mac_做测试的喵酱的博客-CSDN博客 Docker 容器使用 | 菜鸟教程 一、docker介绍 1.1 介绍 Docker 属于 Linux 容器的一种封装&#xff0c;提供简单易用的容…

LeetCode——两数相加

目录 一、两数相加 1、题目 2、题目解读 3、代码 二、反转链表 1、题目 2、题目解读 3、代码 三、两数相加 II 1、题目 2、题目解读 3、代码 反转链表再进行计算 借助栈 一、两数相加 1、题目 2. 两数相加 - 力扣&#xff08;Leetcode&#xff09; 给你两个 非…

linux入门之进程控制(上)进程创建,进程等待

目录 一、进程创建 1.fork函数 2.fork函数返回值 3.写时拷贝 4.fork常规用法 5.fork调用失败原因 二、进程终止 1.进程退出场景 2.进程常见退出方法 2.1_exit函数&#xff08;直接调用内核&#xff09; 2.2 exit函数 2.3return退出 三、进程等待 1.进程等待必要性…

Mysql数据库插入数据时提示 1366 - Incorrect string value: ‘\xE5\xBC\xA0\xE4\xB8\x89‘ 报错

背景&#xff1a; 在部署完成了mysql5.7.4数据库服务之后创建了mydb数据库&#xff0c;然后创建了如下的表&#xff1a; CREATE TABLE department(did int (4) NOT NULL PRIMARY KEY, dname varchar(20) );CREATE TABLE employee (eid int (4) NOT NULL PRIMARY KEY, ename var…