JVM垃圾回收机制

一、如何判断对象可以可以被回收

1.1 引用计数法

定义:只要一个对象被变量所引用,则该对象计数就+1,若被引用了两次,则它的引用计数就变为2,如果某一个变量不再引用它了,则它的引用计数就减一,当该对象的引用变为0的时候就表示没有变量引用它了,该对象就可以被当作垃圾回收了。

弊端:当两个对象循环引用时候,但是又没有被别的变量引用,并且这两个对象不在有实用价值,这时,垃圾回收并不能够回收这两个对象,可能导致內存溢出。

1.2 可达性分析算法

根对象:那些肯定不能被当成垃圾回收的对象

定义:在垃圾回收之前,先对堆中的所有对象进行一次扫描,判断每一个对象是否被根对象直接或者间接的引用,如果是,则不能被回收,反之则可以被当成垃圾回收

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

扫描堆中的对象,看是否能够沿着GC Root对象(一系列对象)为起点的引用链找到该对象,找不到,表示可以回收

哪些对象可以作为GC Root ?

虚拟机栈(栈帧中的本地变量表)中引用的对象。
方法区中类静态属性引用的对象。.

方法区中常量引用的对象。
本地方法栈中JNI(即-般说的Native方法)引用的对象。

1.3 四种引用

1.强引用
只有所有GC Roots对象都不通过[强引用]引用该对象,该对象才能被垃圾回收
2.软引用(SoftReference)
仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用对象
可以配合引用队列来释放软引用自身
3.弱引用(WeakReference)
仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象
可以配合引用队列来释放弱引用自身
4.虚引用(PhantomReference)
必须配合引用队列使用,主要配合ByteBuffer 使用,被引用对象回收时,会将虚引用入队,由Reference Handler线程调用虚引用相关方法释放直接内存
5.终结器引用(FinalReference)
无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象暂时没有被回收), 再由Finalizer线程通过终结器引用找到被引用对象并调用它的finalize方法,第二次GC时
才能回收被引用对象

二、垃圾回收算法

2.1 标记清除

特点:速度较快、会造成內存碎片

JVM垃圾回收机制

2.2 标记整理

特点:速度慢、没有內存碎片

JVM垃圾回收机制

2.3 复制

特点:没有內存碎片、需要占用双倍内存空间

JVM垃圾回收机制

三、分代垃圾回收

3.1 分代垃圾回收机制

对象首先分配在伊甸园区域
新生代空间不足时,触发minor gc,伊甸园和from存活的对象使用copy复制到to中,存活的对象年龄加1并且交换from to
minor gc会引发stop the world, 暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行
当对象寿命超过阈值时,会晋升至老年代,最大寿命是15 (4bit)
当老年代空间不足,会先尝试触发minor gc,如果之后空间仍不足,那么触发full gc, stw的时间更长

3.2 相关VM参数

四、垃圾回收器

4.1串行

单线程
堆内存较小,适合个人电脑

JVM垃圾回收机制

4.2吞吐量优先

多线程
堆内存较大,多核cpu
让单位时间内,STW的时间最短0.2 0.2=0.4

JVM垃圾回收机制

4.3响应时间优先

多线程
堆内存较大,多核cpu
尽可能让单次STW的时间最短0.1 0.10.1 0.10.1=0.5

JVM垃圾回收机制

4.4G1

适用场景
同时注重吞吐量(Throughput) 和低延迟(Low latency) ,默认的暂停目标是200 ms
超大堆内存,会将堆划分为多个大小相等的Region
整体上是标记+整理算法,两个区域之间是复制算法
相关JVM参数
-XX:UseG1GC
-XX:G1HeapRegionSize=size
-XX:MaxGCPauseMillis=time

G1垃圾回收阶段

1)Young Collection

2)Young Collection + CM

在Young GC时会进行GC Root的初始标记
老年代占用堆空间比例达到阈值时,进行并发标记(不会STW),由下面的JVM参数决定
-XX:InitiatingHeapOccupancyPercent=percent (默认45%)

3)Mixed Collection

会对E、S、O进行全面垃圾回收
最终标记(Remark) 会STW
拷贝存活(Evacuation) 会STW
– XX:MaxGCPauseMillis=ms

JVM垃圾回收机制

FullGC

■SerialGC
新生代内存不足发生的垃圾收集- minor gc
老年代内存不足发生的垃圾收集- full gc
■ParallelGC
新生代内存不足发生的垃圾收集- minor gc
老年代内存不足发生的垃圾收集- full gc
■CMS
新生代内存不足发生的垃圾收集- minor gc
老年代内存不足:并发失败以后,才叫Full GC,否则不会触发Full GC
■G1
新生代内存不足发生的垃圾收集- minor gc
老年代内存不足:当老年代內存跟堆內存占比达到45%以上,会触发并发标记的阶段,以及后续混合收集的阶段,如果垃圾回收的速度比新产生的垃圾的速度要快,来的及打扫,这是还不叫Full GC,还是并发垃圾回收的阶段(也会有暂停,但是时间很短), 当垃圾回收的速度跟不上垃圾产生的速度,并发收集就会失败,转化为Full GC(并发进行),stw时间也会更长。

Young Collection 跨代引用

卡表与Remembered Set
在引用变更时通过post-write barrier + dirty card queue
concurrent refinement threads更新Remembered Set

JVM垃圾回收机制

Remark

JDK 8u20 字符串去重

优点:节省大量内存
缺点:略微多占用了cpu时间,新生代回收时间略微增加
-XX: +UseStringDeduplication 默认开启

将所有新分配的字符串放入一个队列
当新生代回收时, G1并发检查是否有字符串重复
如果它们值一样,让它们引用同-一个char[]
注意,与String. intern()不- -样
String. intern()关注的是字符串对象
而字符串去重关注的是char[]
在JVM内部,使用了不同的字符串表

JDK 8u40 并发标记类卸载

所有对象都经过并发标记后,就能知道哪些类不再被使用,当一个类加载器的所有类都不再使用,则卸载它
所加载的所有类
-XX:+ClassUnloadingWi thConcurrentMark默认启用

JDK 8u60 回收巨型对象

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

JVM垃圾回收机制

JDK 9 并发标记起始时间的调整

并发标记必须在堆空间占满前完成,否则退化为FullGC
JDK9之前需要使用-XX:InitiatingHeap0ccupancyPercent
JDK 9可以动态调整
-XX: Initiat ingHeapOccupancyPercent用来设置初始值
进行数据采样并动态调整
总会添加一个安全的空档空间

JDK 9 更高效的回收

250+增强

180+bug修复

五、垃圾回收调优

预备知识
掌握GC相关的VM参数,会基本的空间调整
掌握相关工具
明白一点:调优跟应用、环境有关,没有放之四海而皆准的法则

5.1 调优领域

■内存
■锁竞争
■cpu占用
■io

5.2 确定目标

[低延迟]还是[高吞吐量],选择合适的回收器
CMS,G1(JDK9,在更大的內存下工作的比CMS要好),ZGC (低延迟)
ParallelGC (高吞吐量)

Zing(stw 0停顿、可管理超大的內存)

互联网项目主要是针对 低延迟

5.3 最快的GC是不发生GC

查看FullGC前后的内存占用,考虑下面几个问题
数据是不是太多?

resultSet = statement.executeQuery(“select * from大表limit n”)
数据表示是否太臃肿?

对象图
对象大小16 Integer 24 int 4
是否存在内存泄漏?

static Map map =


第三方缓存实现

5.4 新生代调优

新生代的特点

所有的new操作的内存分配非常廉价
TLABlthread-local allocation buffer
死亡对象的回收代价是零
大部分对象用过即死
Minor GC的时间远远低于Full GC

新生代能容纳所有[并发量* (请求-响应)]的数据

幸存区大到能保留[当前活跃对象+需要晋升对象]

升阈值配置得当,让长时间存活对象尽快晋升
-XX:MaxTenuringThreshold=threshold
-XX:+PrintTenuringDistribution

JVM垃圾回收机制

5.5 老年代调优

以CMS为例

■CMS的老年代内存越大越好
■先尝试不做调优,如果没有Full GC那么已经…否则先尝试调优新生代
■观察发生Full GC时老年代内存占用,将老年代内存预设调大1/4~ 1/3
■-XX: CMSInitiatingOccupancyFraction=percent

5.6 调优案例

案例1 Full GC和Minor GC频繁

分析:GC频繁说明空间紧张,究竟是哪一空间紧张呢,如果是新生代,当业务高峰期来了,大量的对象被创建,新生代的空间很快就满了,幸存区的空间紧张,那么它的最大晋升阈值就会降低,导致很多本来生存周期很短的对象,也会晋升到老年代去,这样情况就进一步恶化,这样就导致老年代的Full GC频繁发生。

解决:通过检测工具去观察堆空间的大小,发现新生代的內存设置的太小了,先试着增大新生代的內存,新生代的內存充裕了之后,新生代的垃圾回收就变得不那么频繁了,同时增大了幸存区的空间,以及晋升阈值,这样就能够使得生命周期较短的对象尽可能的留在新生代里,这样就可以让老年代的FullGC也不那么频繁了。

案例2 请求高峰期发生Full GC,单次暂停时间特别长(CMS)

分析:已确定垃圾回收器是CMS,先去查看GC日志,看看CMS哪个阶段耗时比较长,CMS在 重新标记的时候要扫描整个堆内存,如果业务高峰期的时候,新生代的对象较多标记时间会变的很长。

解决:在重新标记之前对新生代先做一次垃圾回收,减少新生代对象的数量,这样就可以减少在重新标记时所耗费的时间。 – XX:+CMSScavengeBeforeRemark

案例3 老年代充裕情况下发生Full GC(JDK1.7 CMS)

分析:之前介绍过CMS可能由于空间不足,导致并发失败,或者是空间碎片比较多,会产生Full GC;若日志排查后,在GC日志里没有并发失败,碎片过多的错入提示,说明老年代的空间是充裕的,不是老年代空间不足产生的Full GC。从案例中可知项目所用的JDK为1.7,JDK1.8是元空间作为方法区的实现,JDK1.7及以前是永久代作为方法区的实现,JDK1.7以前的永久代空间不足也会导致Full GC,JDK1.8以后,元空间不再由JAVA控制,元空间的默认情况下他的內存空间是使用了操作系统的內存空间,空间的容量一般是比较充裕的。而JDK1.7以前永久代的空间如果设小了,就会导致触发整个堆的Full GC。

解决:增大永久代的最大值和初始值。

Original: https://www.cnblogs.com/yclblogs/p/15925418.html
Author: 阿龙同学
Title: JVM垃圾回收机制

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/588925/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • JavaWeb作用域

    JavaWeb作用域 综述 javaweb有四个不同级别的作用域,分别是page页面级别、一次请求级别、一次会话级别、整个web级别。page没有进行实现 请求级别 只对一次req…

    Java 2023年6月8日
    084
  • 一文搞懂 Spring事务是怎么通过AOP实现的 ,让你醍醐灌顶

    阅读此文章需要掌握一定的AOP源码基础知识,可以更好的去理解事务,我在另外一篇文章有提过。 spring事务其实就是根据事务注解生成代理类,然后在前置增强方法里获取connecti…

    Java 2023年6月7日
    086
  • 为什么构造器私有之后不能创建对象了?

    在Java中,我们可以构造方法,而构造方法又分为有参和无参构造方法: /* 无参数的构造方法,如果类中无构造方法,则默认就有一个无参的构造方法,如果类中有带参的构造方法,则默 * …

    Java 2023年6月8日
    064
  • Linux命令拾遗-使用blktrace分析io情况

    原创:打码日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处。 简介 一般来说,想检查磁盘I/O情况,可以使用iostat、iotop、sar等,但这些命令只能做一…

    Java 2023年6月7日
    081
  • NotePad++的基本使用方法

    第一步:下载完成后的基本设置 设置>>首选项 进行如下操作 这个设置主要是为了在NotePad++的页面中可以输入汉字 第二步:在文件夹中新建文本文档 将后面的后缀改成…

    Java 2023年6月16日
    089
  • Centos7安装jdk1.8

    创建文件夹 mkdir /usr/local/java/ 解压你的压缩包 unzip xxx.zip -d /usr/local/java/ 设置环境变量 vim /etc/pro…

    Java 2023年6月13日
    086
  • jquery 跨域调用wcf 返回json 碰到的一些问题

    走到这里的时候,发现网络上能学习的资源或是比较适合自己项目的文章越来越少了,也在这里停留了比较长的时间 在做跨域的过程中,感觉http://localhost:9090/域与htt…

    Java 2023年6月13日
    070
  • 【金九银十必问Java面试题】工作六年面试被问JVM为什么使用元空间替换了永久代?

    “JVM 为什么使用元空间替换了永久代?”这是一个工作6年的同学去字节第一面遇到的问题,很遗憾,他没有回答出来大家好,我是Mic,一个工作了14年的Java…

    Java 2023年6月16日
    084
  • springboot 整合 jsr-303 数据校验

    数据校验 element前端自定义校验规则 :rules=”dataRule” 绑定数据校验规则方法 * firstLetter: [ { validato…

    Java 2023年6月9日
    066
  • SpringMVC

    SpringMVC 本文分为以下几个部分: Controller 和 RestController RequestParam 和 RequestBody 结语 Controller…

    Java 2023年6月5日
    078
  • 【Q&A】sln 修改导致linux所有命令无法使用

    场景 在Centos 服务器上按照 abp命令,然后执行 abp –version命令报一下错误: abp: relocation error: /lib64/libc.so.6…

    Java 2023年6月8日
    080
  • CURL详解

    参数组参数描述 url url 需要抓取的一到多个URLs; 多个下面通配符的方式: 1、http://{www,ftp,mail}.aiezu.com; 2、http://aie…

    Java 2023年6月8日
    092
  • JAVA基础学习第六天!

    精华笔记: 1.数组: -复制: -System.arraycopy(a,1,b,0,4); -int[] b = Arrays.copyOf(a,6); a = Arrays.c…

    Java 2023年6月13日
    061
  • 甲骨文严查Java授权,换openJDK要避坑

    背景 外媒The Register报道,甲骨文稽查企业用户,近期开始将把过去看管较松散的Java授权加入。 甲骨文针对标准版Java(Java SE)有2种商业授权。2019年4月…

    Java 2023年5月29日
    068
  • Java代码结构简述

    结构 顺序结构 JAVA的基本结构就是顺序结构,除非特意指明,否则就按照顺序,一句一句执行。 顺序结构是最简单的算法结构 语句与语句之间,框与框之间按照从上到下的顺序执行, *它是…

    Java 2023年6月9日
    090
  • Jvm调优

    首先我们可以使用各种JVM工具,查看当前日志,分析当前JVM参数设置,并且分析当前堆内存快照和gc日志,根据实际的各区域内存划分和GC执行时间,觉得是否进行优化,当然我们也可以直接…

    Java 2023年6月13日
    049
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球