JVM篇之垃圾回收

news2024/11/25 14:44:55

一.如何判断对象可以回收

1.引用计数法

只要一个对象被其他变量所引用,就让它的计数加1,被引用了两次就让它的计数变成2,当这个变量的计数变成0时,就可以被垃圾回收;

弊端:当出现如下图的情况,A对象引用了B对象,B对象也引用了A对象,所以A,B的计数均为1,但是没有其他的引用它们俩;虽然应该被垃圾回收,但是因为计数不为0,则无法进行回收

在这里插入图片描述

2.可达性计数法

  • Java 虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象

  • 扫描堆中的对象,看是否能够沿着 GC Root对象(根对象) 为起点的引用链找到该对象,找不到,表示可以回收**(就是先确定好一定不会被回收的对象作为根对象,然后查找目标对象是否直接或者间接被根对象所引用)**

  • 哪些对象可以作为 GC Root ?

    • GCRoot对象包括:虚拟机栈终点局部变量表引用的对象,方法区中类静态属性引用和常量引用对象,本地方法栈中的对象

五种引用(面试常考)

在这里插入图片描述

1) 强引用

只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收

如上图、只有B、C对象都不引用A1对象时,A1对象才会在垃圾回收时被回收;

2) 软引用(SoftReference)

  • 仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用对象(实在不行了才回收软引用对象)
  • 可以配合引用队列来释放软引用自身(因为软引用对象自身也是占一点内存的)

软引用的使用:

在这里插入图片描述
在这里插入图片描述
可见前四次已经被回收了

引用队列的使用:

如果在垃圾回收时发现内存不足,在回收软引用所指向的对象时,软引用本身不会被清理(就是上图结果中的null值)

如果想要清理软引用,需要使用引用队列

在这里插入图片描述

3) 弱引用(WeakReference)

仅有弱引用引用该对象时,在垃圾回收时,(full gc时)无论内存是否充足,都会回收弱引用对象

可以配合引用队列来释放弱引用自身

在这里插入图片描述

结合引用队列同软引用相似

4) 虚引用(PhantomReference)

必须配合引用队列使用,主要配合 ByteBuffer 使用,被引用对象回收时,会将虚引用入队由 Reference Handler 线程调用虚引用相关方法释放直接内存

5) 终结器引用(FinalReference)

无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 finalize方法,第二次 GC 时才能回收被引用对象

二.垃圾回收算法(重要)

1.标记清除

定义:标记清除算法顾名思义,是指在虚拟机执行垃圾回收的过程中,先采用标记算法确定可回收对象,然后垃圾收集器根据标识清除相应的内容,给堆内存腾出相应的空间

在这里插入图片描述

清除后,对于腾出内存空间并不是将内存空间的字节清0,而是会把被清除对象所占用内存的起始结束的地址记录下来,放入空闲的地址列表中,下次分配内存的时候,再选择合适的位置存入,直接覆盖

优点:速度快;

缺点:容易产生大量的内存碎片,可能无法满足大对象的内存分配,一旦导致无法分配对象,那就会导致jvm启动gc,一旦启动gc,我们的应用程序就会暂停,这就导致应用的响应速度变慢

2.标记整理

标记-整理 会将不被GC Root引用的对象回收,清楚其占用的内存空间。然后整理剩余的对象,速度慢、可以有效避免因内存碎片而导致的问题,但是因为整体需要消耗一定的时间,所以效率较低

3.复制算法

复制算法将内存分为等大小的两个区域,FROM和TO(TO中始终为空)。先将被GC Root引用的对象从FROM放入TO中,再回收不被GC Root引用的对象。然后交换FROM和TO。这样也可以避免内存碎片的问题,但是会占用双倍的内存空间。

在这里插入图片描述

三.分代垃圾回收

1.回收流程

① 新创建的对象都被放在新生代的伊甸园(Eden)中

在这里插入图片描述

② 当伊甸园空间不足时,会采用复制算法进行垃圾回收,这时的回收叫做Minor GC;把伊甸园和幸存区From存活的对象先复制到幸存区To中,此时存活的对象寿命+1,并清理掉未存活的对象,最后再交换幸存区From和幸存区To;

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

③ 再次创建对象,若新生代的伊甸园又满了,则同上;

④ 如果经历多次垃圾回收,某一对象均未被回收,寿命不断+1,当寿命达到阈值时(最大为15,4bit)就会被放入老年代中;

在这里插入图片描述

⑤ 如果老年代中的内存都满了,就会先触发Minor GC 如果内存还是不足,则会触发Full GC,扫描新生代和老年代中所有不再使用的对象并回收、

总结

  • 对象首先分配在伊甸园区域

  • 新生代空间不足时,触发 minor gc,伊甸园和 from 存活的对象使用 copy 复制到 to 中,存活的对象年龄加 1并且交换 from to

  • minor gc 会引发 stop the world,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行

  • 当对象寿命超过阈值时,会晋升至老年代,最大寿命是15(4bit)

  • 当老年代空间不足,会先尝试触发 minor gc,如果之后空间仍不足,那么触发 full gc,STW的时间更长

2.GC 分析

1) 大对象处理策略

当遇到一个较大的对象时,就算新生代的伊甸园为空,也无法容纳该对象时,会将该对象直接晋升为老年代

2) 线程内存溢出(OOM)

某个线程的内存溢出了而抛异常(out of memory),不会让其他的线程结束运行

这是因为当一个线程抛出OOM异常后,它所占据的内存资源会全部被释放掉,从而不会影响其他线程的运行,进程依然正常

四.垃圾回收器


串行
  • 单线程

  • 堆内存较小,适合个人电脑


吞吐量优先

  • 多线程

  • 堆内存较大,多核 cpu

  • 让单位时间内,STW 的时间最短 0.2 0.2 = 0.4,垃圾回收时间占比最低,这样就称吞吐量高(少餐多食


响应时间优先

  • 多线程

  • 堆内存较大,多核 cpu

  • 尽可能让单次 STW 的时间最短 0.1 0.1 0.1 0.1 0.1 = 0.5(少食多餐


1.串行

在这里插入图片描述

安全点:

让其他线程都在这个点停下来,以免垃圾回收时移动对象地址,使得其他线程找不到被移动的对象;因为是串行的,所以只有一个垃圾回收线程。且在该线程执行回收工作时,其他线程进入阻塞状态

Serial 收集器:

Serial收集器是最基本的、发展历史最悠久的收集器

特点:单线程、简单高效(与其他收集器的单线程相比),用于新生代采用复制算法。对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。
收集器进行垃圾回收时,必须暂停其他所有的工作线程,直到它结束(Stop The World)

Serial Old 收集器:

Serial Old是Serial收集器的老年代版本

特点:同样是单线程收集器,采用标记-整理算法

2.吞吐量优先

-XX:+UseParallelGC ~ -XX:+UserParallelOldGC:

开启吞吐量优先的回收器,1.8版本默认开启;UseParallelGC是新生代的,采用复制算法;UserParallelOldGC是在老年代的,采用标记整理算法

在这里插入图片描述

-XX:+UseAdaptiveSizePolicy

开启这个将采用自适应的大小调整策略,调整新生代的大小,包括堆的大小和晋升老年代的阈值大小等;种调节方式称为GC的自适应调节策略

-XX:GCTimeRatio=ratio

调整吞吐量的目标,即垃圾回收的时间与总时间的占比(1/(1+ratio)),默认ratio=99,1/(1+ratio))= 0.01,即垃圾回收的时间不能超过总时间的1%(比如总时间100分钟,垃圾回收的时间不能超过1分钟,如果超过1分钟,则GC会自适应的调整大小)

-XX:MaxGCPauseMillis=ms

指的是暂停的毫秒数,默认200ms,即上图红线(和Ratio对立,折中选取

-XX:ParallelGCThreads=n

设置垃圾回收时运行的线程数

3.响应时间优先(CMS)

在这里插入图片描述

1) CMS 收集器

Concurrent Mark Sweep,一种以获取最短回收停顿时间为目标的老年代收集器

特点:基于标记-清除算法实现。并发收集、低停顿,但是会产生内存碎片

应用场景:适用于注重服务的响应速度,希望系统停顿时间最短,给用户带来更好的体验等场景下。如web程序、b/s服务

2) CMS的运行过程(4步)

初始标记:标记GC Roots能直接到的对象。速度很快但是仍存在Stop The World问题

并发标记:进行GC Roots Tracing 的过程,找出存活对象且用户线程可并发执行

重新标记:为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录。仍然存在Stop The World问题

并发清除:对标记的对象进行清除回收

CMS收集器的内存回收过程是与用户线程一起并发执行的

图解:

在这里插入图片描述

4.G1(Garbage First)

JDK1.9默认采用G1垃圾回收器,且废弃的CMS回收器

1) 适用场景

  • 同时注重吞吐量(Throughput)和低延时(Low latency),默认的暂停目标是200ms

  • 超大堆内存,会将堆划分为多个大小相等的区域,称为Region,每个区域都可以独立的作为伊甸园或者新生代或者老年代

  • 整体上是标记-整理算法,在两个区域之间是复制算法

2) 相关jvm参数

在这里插入图片描述

3) G1垃圾回收阶段

在这里插入图片描述

新生代伊甸园垃圾回收—–>内存不足,新生代回收+并发标记—–>回收新生代伊甸园、幸存区、老年代内存——>新生代伊甸园垃圾回收(重新开始)

Young Collection

在这里插入图片描述

Young Collection + CM

  • 在 Young GC 时会进行 GC Root 的初始标记

  • 并发标记(CM)是从GC Root出发顺着其引用链标记其他对象

  • 老年代占用堆空间比例达到阈值时,进行并发标记(不会 STW),由下面的 JVM 参数决定

-XX:InitiatingHeapOccupancyPercent=percent (默认45%)如下图 O 占总的45%时进行并发标记

在这里插入图片描述

Mixed Collection

会对 E、S、O 进行全面垃圾回收

  • 最终标记 会STW

  • 拷贝存活 会 STW

-XX:MaxGCPauseMillis=ms

在这里插入图片描述

黑线:即新生代回收,包括伊甸园、幸存区

红线:复制老年代

:为什么有的老年代被拷贝了,有的没拷贝?

:因为指定了最大停顿时间,如果对所有老年代都进行回收,耗时可能过高。为了保证时间不超过设定的停顿时间,会回收最有价值的老年代(回收后,能够得到更多内存)

4) Full GC

G1在老年代内存不足时(老年代所占内存超过阈值)

如果垃圾产生速度慢于垃圾回收速度,不会触发Full GC,还是并发地进行清理

如果垃圾产生速度快于垃圾回收速度,便会触发Full GC,退化到串行,STW时间会很长

5) Young Collection跨代引用

即老年代引用新生代的问题,因为在新生代回收过程中会沿着GC Root去标记,这些GC Root可能存在于老年代中,而一般老年代中的对象较多,这样每次都需要遍历大量对象;

解决方法:

  • 卡表与Remembered Set:

    • Remembered Set 存在于E中,用于保存新生代对象对应的脏卡,将来对新生代进行垃圾回收时,会根据Remembered Set判断有哪些脏卡,然后再从这些脏卡开始

    • 脏卡:O被划分为多个区域(一个区域512K),如果该区域引用了新生代对象,则该区域被称为脏卡

  • 在引用变更时通过post-write barried + dirty card queue,相当于更新脏卡

  • concurrent refinement threads 更新 Remembered Set

在这里插入图片描述

6) Remark重标记

黑色:已被处理,需要保留的

灰色:正在处理中的

白色:还未处理的

在这里插入图片描述

① 这个时候将处理B,处理结果如下:

在这里插入图片描述

② 下一步将处理C,此时正在并发执行标记,用户线程可能改变B和C的引用关系,会产生如下情况:

在这里插入图片描述

③ 此时C已经被处理完了,被标记成了白色,但是用户线程可能在这时又改变了C的引用

在这里插入图片描述

④ 此时C应该被标记成黑色,但是标记动作已经结束,所以会产生误差

解决误差方法:采用Remark

① 之前C未被引用,这时A引用了C,就会给C加一个写屏障,写屏障的指令会被执行(只有发生引用改变就会执行),将C放入一个队列当中,并将C变为处理中状态

② 在并发标记阶段结束以后,重新标记阶段会STW,然后将放在该队列中的对象重新处理发现有强引用引用它,就会处理它

在这里插入图片描述

7) JDK8u20字符串去重

① 将所有新分配的字符串(底层是char[])放入一个队列

② 当新生代回收时,G1并发检查是否有重复的字符串

③ 如果字符串的值一样,就让他们引用同一个字符串对象

在这里插入图片描述

注意,其与String.intern的区别:

  • intern关注的是字符串对象

  • 字符串去重关注的是char[]

  • 在JVM内部,使用了不同的字符串标

优点与缺点:

  • 节省了大量内存

  • 新生代回收时间略微增加,导致略微多占用CPU

8) JDK 8u40 并发标记类卸载

在并发标记阶段结束以后,就能知道哪些类不再被使用。如果一个类加载器的所有类都不在使用,则卸载它所加载的所有类

-XX:+ClassUnloadingWithConcurrentMark 默认启用

9) JDK 8u60 回收巨型对象

  • 一个对象大于region的一半时,就称为巨型对象
  • G1不会对巨型对象进行拷贝
  • 回收时被优先考虑
  • G1会跟踪老年代所有incoming引用,如果老年代incoming引用为0的巨型对象就可以在新生代垃圾回收时处理掉

在这里插入图片描述

10) JDK9并发标记起始时间的调整

4)提到当垃圾产生速度快于垃圾回收速度,便会触发Full GC,STW的时间就会变长,这个时候就可以通过调整提前开始并发标记来优化

  • 并发标记必须在堆空间占满前完成,否则退化为 FullGC

  • JDK 9 之前需要使用 -XX:InitiatingHeapOccupancyPercent

  • JDK 9 可以动态调整

-XX:InitiatingHeapOccupancyPercent 用来设置初始值

进行数据采样并动态调整

总会添加一个安全的空档空间

五. 垃圾回收调优

1.确定调优领域

  • 内存
  • 锁竞争
  • CPU占用
  • IO
  • GC

2.确定目标

低延迟/高吞吐量? 选择合适的GC

  • CMS(JDK8默认)、 G1(JDK9推荐) 、ZGC(JDK12体验)
  • ParallelGC(高吞吐量)
  • Zing(另一种虚拟机)

3.最快的GC是不发生GC

首先排除减少因为自身编写的代码而引发的内存问题

  • 查看Full GC前后的内存占用,考虑以下几个问题

  • 数据是不是太多?(比如select *)

  • 数据表示是否太臃肿

    • 对象图
    • 对象大小(比如用Integer换成int会小很多)
  • 是否存在内存泄漏(采用软、弱引用;第三方缓存实现等)

4.新生代调优

新生代的特点:

  • 所有的new操作分配内存都是非常廉价的

  • 死亡对象回收零代价

  • 大部分对象用过即死(朝生夕死)

  • MInor GC 所用时间远小于Full GC

:新生代内存越大越好么?

:不是

  • 新生代内存太小:频繁触发Minor GC,会STW,会使得吞吐量下降

  • 新生代内存太大:老年代内存占比有所降低,会更频繁地触发Full GC。而且触发Minor GC时,清理新生代所花费的时间会更长

  • 新生代内存设置为能容纳所有【并发量*(请求-响应)】的数据为宜

5.幸存区调优

  • 幸存区需要能够保存 当前活跃对象+需要晋升的对象

  • 晋升阈值配置得当,让长时间存活的对象尽快晋升

-XX:MaxTenuringThreshold=threshold
-XX:+PrintTenuringDistribution

6.老年代调优

以 CMS 为例

  • CMS 的老年代内存越大越好

  • 先尝试不做调优,如果没有 Full GC 那么程序已经执行的很ok了,否则先尝试调优新生代

  • 观察发生 Full GC 时老年代内存占用,将老年代内存预设调大 1/4 ~ 1/3

-XX:CMSInitiatingOccupancyFraction=percent

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

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

相关文章

4.OCR文本识别Connectionist Temporal Classification(CTC)算法

文章目录1.基础介绍2.Connectionist Temporal Classification(CTC)算法2.1 什么是Temporal Classification2.2 CTC问题描述2.2关于对齐2.3 前向后向算法2.4 推理时3.pytorch中的CTCLOSS参考资料欢迎访问个人网络日志🌹🌹知行空间🌹&#x1f3…

【react】react创建项目与引入AntD组件库:

文章目录一、初始化项目:【1】创建项目【2】暴露项目配置文件【3】安装依赖【4】配置less二、快捷键:【1】rcctab三、安装AntD组件库:【1】安装【2】index.js【3】问题:【4】效果:一、初始化项目: 【1】创…

【基于增强上下文注意网络:超分】

Enhanced Context Attention Network for Image Super Resolution (基于增强上下文注意网络的图像超分辨率) 深度卷积神经网络(CNN)极大地提高了图像超分辨率(SR)的性能。尽管图像随机共振的目标是恢复高频…

Mysql视图,存储过程,触发器,函数以及Mysql架构

一,视图视图是基于查询的一个虚拟表 , 也就是将sql语句封装起来, 要用的时候直接调用视图即可, select语句查询的表称为基表, 查询的结果集称为虚拟表, 基本表数据发生了改变, 那么视图也会发生改变, 使用视图就是为了简化查询语句.1.CREATE VIEW view_admin AS SELECT * FROM…

聊一聊过度设计!

文章目录什么是过度设计?过度设计的坏处如何避免过度设计充分理解问题本身保持简单小步快跑征求其他人的意见总结新手程序员在做设计时,因为缺乏经验,很容易写出欠设计的代码,但有一些经验的程序员,尤其是在刚学习过设…

top -p pid为什么超过100%

CPU:Cores, and Hyper-Threading 超线程(Hyper-Threading ) 超线程是Intel最早提出一项技术,最早出现在2002年的Pentium4上。单个采用超线程的CPU对于操作系统来说就像有两个逻辑CPU,为此P4处理器需要多加入一个Logic…

Spring Cache的基本使用与分析

概述 使用 Spring Cache 可以极大的简化我们对数据的缓存&#xff0c;并且它封装了多种缓存&#xff0c;本文基于 redis 来说明。 基本使用 1、所需依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-…

redis秒杀

redis优惠券秒杀 为什么订单表订单ID不采用自增长&#xff1f; id规律性太明显&#xff0c;容易被用户猜测到&#xff08;比如第一天下订单id10&#xff0c;第二天下订单id100&#xff0c;在昨天的1天内只卖出90商品&#xff09;受单表数据量限制&#xff08;订单数据量大&am…

Redis高级删除策略与数据淘汰

第二章&#xff1a;Redis高级 学习目标 目标1&#xff1a;能够说出redis中的数据删除策与略淘汰策略 目标2&#xff1a;能够说出主从复制的概念&#xff0c;工作流程以及场景问题及解决方案 目标3&#xff1a;能够说出哨兵的作用以及工作原理&#xff0c;以及如何启用哨兵 …

《分布式技术原理与算法解析》学习笔记Day23

分布式数据复制 我们在进行分布式数据存储设计时&#xff0c;通常会考虑对数据进行备份&#xff0c;以提高数据的可用性和可靠性&#xff0c;“数据复制技术”就是实现数据备份的关键技术。 什么是数据复制技术&#xff1f; 在分布式数据库系统中&#xff0c;通常会设置主备…

Java StringBuilder类(为什么更高效、常用方法、构造器)

StringBuilder类一、引言二、StringBuilder类特点三、StringBuilder构造方法四、StringBuilder常用方法五、StringBuilder为什么高效率六、StringBuffer类一、引言 为什么要引入StringBuilder类 提高字符串操作效率&#xff0c;尤其是涉及大量字符串拼接 //获取1970年1月1日0时…

2023年三月份图形化一级打卡试题

活动时间 从2023年3月1日至3月21日&#xff0c;每天一道编程题。 本次打卡的规则如下&#xff1a; 小朋友每天利用10~15分钟做一道编程题&#xff0c;遇到问题就来群内讨论&#xff0c;我来给大家答疑。 小朋友做完题目后&#xff0c;截图到朋友圈打卡并把打卡的截图发到活动群…

土著刷题新功能上线:根据每日学习时间,制定专属学习计划,多种数据统计维度追踪计划进度

吐槽社区的一个小伙伴在用土著刷题微信小程序进行碎片化学习&#xff0c;同时进行多科目题库练习的时候&#xff0c;不好追踪学习进度&#xff0c;希望有个能制定每日目标的功能。因此土著刷题微信小程序v1.11迭代开发了 学习计划 模块功能&#xff0c;学习计划是一个可以设置题…

HTML#3图片,音频,视频标签

一. 介绍1.标签与描述标签 描述<img> 定义图片<audio> 定义音频<video> 定义视频2.标签的详细介绍img:定义图片src:规定显示图像的 URL (统一资源定位符)height:定义图像的高度width:定义图像的宽度audio: 定义音频。支持的音频格式: MP3、WAV、OGGsrc:规定音…

角度制与弧度制的相互转换np.deg2radnp.rad2deg

【小白从小学Python、C、Java】【计算机等级考试500强双证书】【Python-数据分析】角度制与弧度制的相互转换np.deg2radnp.rad2deg选择题以下关于python代码表述错误的一项是?import numpy as npprint("【执行】np.rad2deg(np.pi)")print(np.rad2deg(np.pi))print(&…

有没有必要考PMP证书?

其实针对有没有必要考试吗&#xff0c;这个可以根本不同行业的人来决定的。 1.高等教育项目管理专业科班出身的人员。 在我国本科学历和硕士研究生学历中&#xff0c;项目管理也有开设。不管以后从事的工作是否为项目管理或其他管理&#xff0c;作为本专业的同学&#xff0c;…

HDMI协议介绍(三)--InfoFrame

目录 Auxiliary Video information (AVI) InfoFrame AVI InfoFrame包结构 Header Body 举个例子 附录 Audio InfoFrame Audio InfoFrame包结构 Header Body Vendor Specific InfoFrame Vendor Specific InfoFrame包结构 Header Body AVI/AUDIO/VSI Infoframe都…

Linux 操作系统原理 — 内存管理 — 虚拟地址空间(x86 64bit 系统)

目录 文章目录目录虚拟地址格式与内核页表&#xff08;四级页表&#xff09;虚拟地址格式与内核页表&#xff08;四级页表&#xff09; 在 x86 64bit 系统中&#xff0c;可以描述的最长地址空间为 2^64&#xff08;16EB&#xff09;&#xff0c;远远超过了目前主流内存卡的规格…

QML 颜色表示法

作者: 一去、二三里 个人微信号: iwaleon 微信公众号: 高效程序员 如果你经常需要美化样式(最常见的有:文本色、背景色、边框色、阴影色等),那一定离不开颜色。而在 QML 中,颜色的表示方法有多种:颜色名、十六进制颜色值、颜色相关的函数,一起来学习一下吧。 老规矩…

华为OD机试用Python实现 -【信号发射和接收】(2023-Q1 新题)

华为OD机试题 华为OD机试300题大纲信号发射和接收题目描述输入描述输出描述说明示例一输入输出说明示例二输入输出说明Python 代码实现代码运行结果代码编写思路华为OD机试300题大纲 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为…