JVM学习-垃圾回收(GC)

2020年02月19日06:03:56,开始学习垃圾回收,学习资料来源(张龙老师的JVM课程)

JVM内存数据区域知识复习

学习垃圾回收之前,要对JVM内部的内存区域有详细的了解。先对相关内存区域的内容知识进行复习和总结,为垃圾回收的学习奠定基础。

灰色区域是线程共享

白色区域为线程隔离

引用占用4个字节,空对象占用8个字节。

方法执行结束后,对应的栈Stack中的变量马上回收,但是Heap中的对象要等到GC来回收。

JVM垃圾回收(Garbage Collection)

理论知识

JVM垃圾回收(GC)模型

  1. 垃圾判断算法
  2. GC算法
  3. 垃圾回收器的实现和选择

垃圾判断算法

  1. 引用计数算法(Reference Counting)
  2. 根搜索算法 (GC Roots Tracing)

引用计数算法

对象循环引用的问题结束: A引用B,B又引用A。就算没有外部引用。但是又状态一直为1,这样的就回收不了了。

当然,还是可以通过其他手段来规避他的这个缺点的。

根搜索算法

![image-20200219070328494](/Users/shangyifeng/Library/Application Support/typora-user-images/image-20200219070328494.png)

方法区

垃圾回收(GC)算法

  1. 标记-清除算法(Mark-Sweep)
  2. 标记-整理算法(Mark-compact)
  3. 复制算法(Copying)
  4. 分代算法(Generational):几乎所有虚拟机都会采用这个算法

为什么复制算法在新生代用的多呢?因为java大多是的java对象都是招生熄灭的。可被回收的并不多。老年代的生命周期较长,所以不方便使用复制算法。

所以老年代采用标记清楚和标记整理算法

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

标记-清楚算法:的执行原理图如下所示

如有以下这些对象

Runtime Stack 虚拟机栈是根搜索算法的入口

先标记

F - J 之间有循环引用。 但是 D-G -F-J-M 都是需要被回收掉的。绿色的都是还要 保留的。

流程执行完之后就是如下所示

再清除

复制(Copying)搜集算法

实现简单,运行高效:但是:需要将内存缩小为原来的一般,代价高昂。

现在主要用来回收 新生代

在老年代中,一般不能直接选用这种算法。(因为老年代的存活率高 )

复制搜集算法的示意图

  1. 把A- C=复制

把相互引用的都给复制

余下的没有被复制,会被连锅端掉

复制搜集算法优点

标记-整理算法

如下为执行示意图

标记移动。腾出来连续的空间。没有内存碎片。就是有点慢。

分代收集算法(Generational Collecting)

新生代 和 老年代 的定义

新生代:部分刚新生的对象,没有用的

老年代:经历多次垃圾回收,还会保留

JVM6时候的参考(现在已经没有永久代了)

为什么说只会浪费百分之十呢,这里二娃给我讲述的

这次 设置了2个Survivor是非常巧妙的。第一次的时候,8+1 被使用。复制完之后。放入另外一个Survivor。然后下一次的时候。8+另外一个1,把对象复制到1上。所以就完成了,轮流替换。每次被闲置的就会只有一个占用百分之10的Survivor来准备接受要复制的对象。over。

JVM启动的时候可以根据程序的需要,选择一定的需求所对应的垃圾收集器和对应的垃圾收集算法。

内存相关概念

内存结构

内存分配

内存回收

强引用,软引用,弱引用,虚引用

软引用:内存不够的时候,一会被会GC,长期不用也会被收集

垃圾收集算法

黄色区域应用在年轻代

灰色区域应用在老年代

GC触发的时机

尽量避免对Full GC的使用,因为全局GC调用的时候,会停滞所有的义务代码执行。

MinorGC是一定会有的。

垃圾回收器

垃圾回收器理论分析

没有万能的垃圾回收器,交互性和吞吐率是不兼容的,没有银弹

垃圾收集器的“并行”和“并发”

让用户线程处于等待状态是一个很恐怖的事情。

收集器

Serial收集器

STW(Stop The World) 暂停所有的工作线程

虚拟机运行Client模式下的默认收集器

单线程的GC。

ParNew收集器

Serial的多线程版本。其他都一样

Parallel Scavenge收集器

允许较长较长时间的STW。

Serial Old收集器

Parallel Old收集器

CMS收集器:实现非常复杂的收集器

综上,PPT 都需要自己详细读读。

再次回顾一下JVM的内存数据区域的分配情况

  1. Oracle的HotSpot虚拟机,将 java虚拟机方法栈和本地方法栈放在同一个地方
  2. 程序计数器是唯一一个没有要求说必须要使用GC垃圾回收的地方。
  3. 白色的地方是线程独有的

总结

  • 垃圾判断算法

    • 引用计数算法
    • 根搜索算法
  • 垃圾回收GC算法(常见的)

    • 标记-清除算法
    • 标记-整理算法
    • 复制算法(8-1-1)
    • 分代算法(综合前面几种,针对不同的类型,采用不同的算法)
  • 垃圾收集器

    • Serial
    • ParNew
    • Parallel Scavenger (PS (jdk1.8默认的新生代垃圾收集器))
    • Parallel Old(ParOld jdk1.8默认的老年代垃圾收集器)
    • Serial Old
    • CMS

Java内存泄露的经典原因

  1. 对象定义在错误的范围(Wrong Scope)
  2. 异常(Exception)处理不当
  3. 集合数据管理不当

对象定义在错误的范围

从成员变量 移动到 局部变量。 (布局就不一样了。变量会被及时回收)

异常处理不当

异常处理的不到位,关闭流的操作,应该放置在finally里面。

集合数据管理不当

垃圾回收日志(GC Log)

上案例:

这里需要注意点是:数组是原生数据类型。 数组里面的默认值都是0。如果是引用类型的数组,里面的值都为null。

程序初始:

平淡无奇

输出结果:平淡无奇:

接下来添加一定的JVM参数,进行启动,就变得大不一样

JVM参数加 -verbise:gc : 添加垃圾回收GC的详细信息

JVM参数加 Xms20M Xmx20m:分别表示堆容量的初始值和最大值,一般都设置为一样。固定位20M

JVM参数加-XMn10M:表示新生代的容量大小为10M

JVM参数加-XX:+PrintGCDetails:打印GC的详细信息

JVM参数加-XX:SurvivorRatio=8:表示Even空间和Survivor的比例是8:1:1

运行结果:只有GC详情,但是未执行GC

这东西都是:-XX:+PrintGCDetails 打印的,还没有展现GC、

在程序中,再加入一行 代码,再加一个数组(大小就到达GC临界点)

运行结果:GC就出来了

随着程序的变化,内存空间的修改。会导致不同的打印结果出现。

GC:表示 minor GC ,后面数据的含义和格式

​ Allocation Failure:分配失败

​ PSYoungGen:年轻代 PS(Parallel Scavenger 收集器)。

​ 执行前-执行后(总容量):(7694k->624k(9216k))

​ 执行GC之前堆的大小-执行后的存活对象的大小(堆的大小):(5656k->4728k(19456k))

4104k对应的,正好是老年代新增的 大小。(从新生代释放出来,进入老年代)

Full GC 会导致程序暂停 (2,2,2,2 会出现Full GC, 2,2,3,3 反而不会进入Full GC)

没进入Full GC的原因:当新生代已经无法容纳要生成的对象的时候,直接放入老年代

验证的概念如下:GC的触发时机

对于网上的文章或者概念,还有书本等等。里面的内容都是不确定是对的,唯一能验证概念的,就是通过代码,通过数据去看结论。

JVM参数

java -XX:+PrintCommandLineFlags -version :输出启动参数和java版本

-XX:+UseParallelGC : 默认使用的GC是 Parallel

-XX:PretenureSizeThreshold=4194304:大小阈值设置,设置超过多大值时直接在老年代进行分配。

超过这个值之后,就不会在新生代去创建,直接进行入老年代

设置好上述参数,然后执行下面程序:

但是:并不是上面的参数结论的作用。所以,一般结论正确与否,还是需要通过代码来确定正确性。

是因为:上述概念是需要使用 Serial 垃圾回收器。

所以再加一个参数:-XX:+UseSerialGC设置项目启动之后使用的GC是Serial垃圾回收器

老年代占用5M:直接就是程序定义的对象的大小:说明直接进入老年代。

使用可视化工具查看垃圾回收

这里可视化工具提供的手动按钮是 Full GC (System.gc())

关于System.gc()的补充,我们手动调用gc或者自己执行gc方法的时候,我们只是告诉了JVM需要执行垃圾回收,但是至于什么时候回收,是由JVM决定的。

如果我们不告诉JVM需要GC的话,只有在创建新的对象的时候才有可能去校验是否需要垃圾回收。因为只有在创建对象的时候会导致堆内存的增加。

这就是System.get()方法存在的意义和价值:在没有对象创建的时候触发垃圾回收。

jmc查看的可视化

FromSurvivor和ToSurvivor的角色的切换

如果频繁切换,那么会通过一定的算法转移到老年代。

-XX:MaxTenuringThreshold=5:在可以自动调节对象晋升(Promote)到老年代阈值的GC中设置该阈值的最大值。

设置可以晋升到老年代的最大存活年龄。该参数的默认值为15,CMS中默认值为6,G1中默认为15(在JVM汇总,该数值是由4个bit来表示的,所以最大值为1111,即15)

-XX:+PrintTenuringDistribution:打印出年龄为n的字节对象

经历了多次GC后,存货的对象会在FromSurvivor和ToSurvivor的角色之间来回切换。而这里面的一个前提则是这两个空间有足够的大小来存放这些数据,在这些算法中。会计算每个对象年龄的大小,如果达到某个年龄后发现总大小已经大于了Survivor空间的50%,那么这时候就需要调整阈值,不能再继续等到默认的15此GC后才完成晋升,因为这样会导致Survivor空间不足,所以需要调整阈值,让这些存货对象尽快完成晋升。

Survivor空间不足是很严重的。

运行结果:

threshold的动态调整

这个时候运行,没有问题。

接下来添加JVM参数

XX:TargetSurviorRatio=60 :占据百分之60,会重新计算阈值

XX:+PrintGCDateStamps:打印JVM运行的时间戳

XX:+UseConcMarkSweepGC: 老年代使用CMS

XX:+UserParNewGC:新生代使用ParNew

XX:MaxTenuringThreshold=3:最大年龄为3,就得去老年代了

运行结果

这里,下面的图,只有age=1的,说明,age=1,=2,=3的已经被全部放入老年代当中。

枚举根节点

OopMap数据结构

安全点: Safepoint

安全点:程序执行并非在所有地方都能停顿下来开始GC,只有在达到安全点时才能暂停。

抢断式中断:JVM中断

主动式中断:轮询标志的地方和安全点是重合的。现在自己中断自己。

安全区域

Safe Region

CMS垃圾回收器详解

将CMS垃圾回收器学完之后,需要大力度的学习一下G1垃圾回收器。

典故:CMS研究的人离职了,新来的人怎么也看不懂C++的源码。

CMS:Concurrent Mark Sweep 并发标记清除

并发的含义:垃圾收集的线程可以和用户的业务线程同时执行。

标记和清除:说明CMS是采用标记和清除来完成的。

标记:标记的是哪些对象依然是属于存活状态。哪些是需要清除的对象。

清除:清除掉垃圾的对象。

初始标记:

并发标记:

重新标记:为了修正并发期间因用户线程继续运作而导致的变动的部分。

并发清除:

CMS收集器运作步骤:

初始标记:STW,没有用户线程

并发标记:可以和用户线程同时执行

重新标记:STW,没有用户标记

并发清理:可以和用户线程同时执行

重置线程:恢复到初始状态

CMS收集器的优点和缺点

优点:

停顿:指的是用户线程停顿

缺点:

空间分配担保:新生代容纳不了,直接扔给老年代,这就叫老年代担保。

CMS收集器收集步骤:

不带Concurrent,会导致用户线程暂停

到此:标记完成。接下来调用清理算法。

用程序去认识CMS垃圾收集器

程序代码

虚拟机参数

运行结果:

JVM学习——垃圾回收GC(学习过程)的更多相关文章

  1. 修改Tomcat的jvm的垃圾回收GC方式为CMS

    修改Tomcat的jvm的垃圾回收GC方式 cp $TOMCAT_HOME/bin/catalina.sh $TOMCAT_HOME/bin/catalina.sh.bak_20170815 vi $ ...

  2. JVM虚拟机垃圾回收(GC)算法及优缺点

    一.什么是GC   GC是jvm的垃圾回收,垃圾回收的规律和原则为:   次数上频繁收集新生区(Young)   次数上较少收集养老区(Old)   基本上不动永久区(Perm) 二.GC算法(分代收 ...

  3. 关于JVM的垃圾回收(GC) 这可能是你想了解的

    目录 1 JVM中Java对象的分类 2 JVM的GC类型及触发条件 2.1 Young GC 2.2 Full GC 3 Java对象生成时的内存申请过程 3 Oracle JDK中的垃圾收集器 3 ...

  4. jvm学习-垃圾回收算法(三)

     垃圾回收算法  引用计数法 比较古老的一种垃圾回收算法.在java的GC并没有采用 增加一个引用 引用+1 减少一个引用引用减一 每次清除引用为0的的对象 缺点:不能回收循环引用的垃圾对象 标记清除 ...

  5. 性能测试三十五:jvm垃圾回收-GC

    垃圾回收-GC 三个问题 哪些内存需要回收? 什么时候回收? 如何回收? YoungGC和FullGC: 新生代引发的GC叫YoungGC 老年代引发的GC叫FullGC FullGC会引起整个Jvm ...

  6. 【转】Java学习---垃圾回收算法与 JVM 垃圾回收器综述

    [原文]https://www.toutiao.com/i6593931841462338062/ 垃圾回收算法与 JVM 垃圾回收器综述 我们常说的垃圾回收算法可以分为两部分:对象的查找算法与真正的 ...

  7. 03 JVM的垃圾回收机制

    1.前言 理解JVM的垃圾回收机制(简称GC)有什么好处呢?作为一名软件开发者,满足自己的好奇心将是一个很好的理由,不过更重要的是,理解GC工作机制可以帮助你写出更好的Java程序. 在学习GC前,你 ...

  8. JVM虚拟机—JVM的垃圾回收机制(转载)

    1.前言 理解JVM的垃圾回收机制(简称GC)有什么好处呢?作为一名软件开发者,满足自己的好奇心将是一个很好的理由,不过更重要的是,理解GC工作机制可以帮助你写出更好的Java程序. 在学习GC前,你 ...

  9. JVM(九):垃圾回收算法

    JVM(九):垃圾回收算法 在本文中,我们将从概念模型的角度探讨 JVM 是如何回收对象,包括 JVM 是如何判断一个对象已经死亡,什么时候在哪里进行了垃圾回收,垃圾回收有几种核心算法,每个算法优劣是 ...

随机推荐

  1. Spring系列2:Spring容器基本概念和使用

    本文内容 简单回顾IoC和DI概念 Spring容器的概念 的xml配置和初始化 容器的基本使用 bean的定义和初始化配置 简单理解IoC和DI概念 什么是IoC控制反转? 通俗地但不严谨地讲,以前 ...

  2. 02-JS中的数据类型及类型转换

    02-JS中的数据类型及类型转换 一.数据类型 JS中的值,无论是字面量还是变量,都有明确的类型. (一)概述 1.基本类型5种 number 数字类型 string 字符串类型 boolean 布尔 ...

  3. Git在实际生产中的使用

    文章目录 Git在实际生产中的使用 简单情况下的代码提交 Fetch and Pull 仅获取某分支的代码 远程仓库已经合并了别人的代码 冲突产生原因与解决办法 不恰当的多个Commit合并为一个 G ...

  4. SQLServer触发器调用JavaWeb接口

    这几天接到一个需求需要吧不同系统的数据库进行同步,需要我做一个中间平台进行连接,瞬间就想到了触发器调用接口然后通过API进行传递再写入另一个数据库. sqlServer触发器调用JavaWeb接口 1 ...

  5. JavaScript 中BOM的常用操作

    JavaScript BOM操作 1.获取浏览器窗口尺寸 var width=window,innerWidth //获取可视窗口宽度 var height=window.innerHeight // ...

  6. 基础概念(3):怎么写一个c程序?

    总结卡片: 遵循c语言的规则,即可写出c程序.规则下有两个重要概念:函数与变量.就好像游戏中的打仗,要考虑怎么打,谁来打."怎么打"就是流程,把流程封装起来就是函数,流程也叫算法. ...

  7. 第02讲:Flink 入门程序 WordCount 和 SQL 实现

    我们右键运行时相当于在本地启动了一个单机版本.生产中都是集群环境,并且是高可用的,生产上提交任务需要用到flink run 命令,指定必要的参数. 本课时我们主要介绍 Flink 的入门程序以及 SQ ...

  8. 字符串自实现(一)(mystrcpy,mystrcat,mystrcmp)

    char* mystrcpy(char* str_one,const char* str_two) { char* tmp = str_one; while (*str_one++ = *str_tw ...

  9. 常见Web服务器

    常见Web服务器

  10. Mac系统之U盘重装(降级)

    U盘重装mac系统步骤如下 1. 将Mac系统U盘插上电脑,按下Mac开机键后,摁住"option"键不放,稍等几秒会出现启动盘选择界面,选择"安装 macOS" ...