本文应该与堆的内存规划合二为一,不过还不知道如何排版,所以目前就先这样子吧

概念:STW,stop the word,指的是当前我们自己的应用线程暂停,但是虚拟机的GC线程依然运行

垃圾回收算法

1.引用计数法

首先,每个对象都有一个与之对应的引用计数器,当其他对象引用该对象的时候,计数器+1,当不再引用该对象时,计数器-1,当引用计数器为0的时候,此时该对象不会再次被引用,等待着被收回的命运,由于引用计数法无法处理循环引用,所以JVM中只有Symbol Table中使用了该算法
Symbol Table:每个常量池都含有字符串,为了节省空间,这些字符串都是存放在一个叫做Symbol Table的内存区域,如果两个类中都有"abc"的字符串,则在Symbol Table中只包含一个"abc",并且被标记引用计数=2,当这两个类都被卸载的时候,"abc"才被删除,而不需要进行可达性分析

2.标记清除法

该算法分为两个阶段,标记阶段和清除阶段
2.1通过根节点,标记所有能通过根节点可以到达的对象,此时没被标记的就是垃圾对象,下图绿色表示有用的对象,灰色表示没用的对象(将被回收)

2.2垃圾回收时清除所有没被标记的对象
下图灰色区域表示已经空闲下来的内存空间

从第二幅图中明显可以看出,回收之后,内存空间并不是连续的,也就是说空间碎片很多,这也是标记清除法的缺点

3.标记压缩法(也叫标记整理法)
如果你玩英雄联盟,那么这个算法特别好理解,因为和纳尔的大招是一样的
该算法克服了上述标记清除法有空间碎片的缺点,它分三个阶段(就是比标记清除法多出一个阶段)
1.标记阶段(和标记清除法相同,可回去看图)
2.清除阶段(和标记清除法相同,可回去看图)
3.将所有存活压缩到内存的另一端(纳尔放大招,直接推到墙上),如下面两幅图所示
当清除阶段完毕之后,压缩之前,内存中是这个样子的

压缩之后,内存中是下面这个样子的,已经没有空间碎片了

标记压缩法适合老年代,相对来说,老年代区绝大多数的对象都是存活的,和复制算法相比,标记压缩法适合存活的对象占据大多数情况,不方便移动到另一个区域,因为老人行动不便…

4.复制算法
该算法的思想是将内存分为两部分A和B,A部分有对象,B部分没有对象,在垃圾回收开始的时候,将A中所有存活对象挪到B中,然后清空A内存中所有对象,此时,A内存空下来,也不会产生碎片的问题,如此往复循环,AB互换,这就是复制算法

复制算法的缺点是将内存减半了,它适合内存中垃圾对象相对较多的情况,因为相对来说,垃圾对象越多,有用对象就越少,这样,挪动的对象就越少,所以该算法适合新生代内存区,因为年轻人爱运动…
下图中表示了复制算法在回收之前AB两部分内存的使用情况
(其中绿色表示活着的对象,对象a-对象k表示垃圾对象)

下图表示垃圾回收之后,AB两部分内存的使用情况

注意:上文图中,出现了根对象,这指的是什么???根对象指的下面3种

1.如果一个对象,当前在某个线程的栈帧中引用它,那么该对象就是根对象
2.如果一个对象,在JVM的方法区中有引用它,那么该对象就是根对象
3.如果一个对象,有用native(JNI)修饰的方法在引用他,那么它就是根对象

这正好解决了内存中,两个对象循环引用的问题

安全点safe point

当执行GC的时候,不是立马就执行的,而是需要一个恰当的时机,这个时机,就叫做安全点,可以理解和坐公交一样,不是说你想下车,就马上下车的,最起码,也要等车到达最近一个公交站点,才允许下车,下面几个位置(时机)可以作为安全点
方法返回之前
调用某个方法之后
抛出异常的位置
循环的末尾
主要是为了保证代码执行的完整度
safepoint逻辑上等同于CyclicBarrie,当要执行GC的时候,安全点打开,所以所有线程都会在执行上述几个位置的时候,进入安全点表,所有线程都跑到安全点之后,GC线程开始执行GC

安全区域safe region

如果一段代码在运行的时候,引用关系不会发生变化,那么在这段代码就是安全区域,而安全区域里的任意位置,都是安全点,比如下面这段代码

Thread.sleep(666);

sleep方法中的任意位置,都是安全点,所以GC的时候,如果某个线程正好处于安全区域,则该线程相当于直接到达了安全点

垃圾收集器

1.Serial
串行垃圾收集器,在垃圾回收阶段(STW阶段)只有一个线程负责垃圾回收,简单而高效,但是现在基本都是多核CPU,不能很好利用多核特性,使用下面命令开启Serial垃圾收集器

// 在新生代使用Serial收集器,采用复制算法
-XX:+UseSerialGC
// 在老年代使用Serial收集器,采用标记压缩法
-XX:+UseSerialOldGC

2.ParNew
与Serial一样,不同点是用来匹配多核CPU的,在垃圾回收阶段(STW阶段)可以多个线程同时垃圾回收,也可以使用-XX:ParallelGCThreads来指定线程数,不过不推荐修改,默认就可以,使用下面命令开启ParNew垃圾收集器

// 该垃圾收集器只适用于年轻代,不能用于老年代
-XX:+UseParNewGC

3.Parallel Scavenge
这个收集器不太了解,我个人理解是应该在时间上可以自由分配,比方说我分配每次GC回收50毫秒,那么这个收集器会在50好秒内尽可能的回收垃圾对象,说白了就是这么长时间内,你能干多少,就干多少,干不完就算了,下次再干, 有点像G1,具体不清楚,待以后研究hotspot的时候再补上

// 在新生代使用Parallel Scavenge收集器,采用复制算法
-XX:+UseParallelGC
// 在老年代使用Parallel Scavenge收集器,采用标记压缩法
-XX:+UseParallelOldGC

4.CMS(Concurrent Mark Sweep)
翻译成中文是并发标记清理,由此可见该收集器是标记清除算法的实现,该收集器的最终目的是STW时间越短越好,且该收集器只用于老年代,具体步骤如下

(1)初始标记阶段,首先STW,然后所有标记root对象,注意,只标记root对象(标记谁就保留谁,没被标记的将会被回收)
(2)并发标记阶段,不会STW,并且标记所有root对象所引用的对象,也就是说顺着root对象找到整个对象引用链,这个过程是CMS收集器最耗时的过程,大约占百分之七八十的时间
笔记:并发两个字指的是找链过程和我们的应用程序,同时执行,所以有可能会出现对象消失,或者浮动垃圾
(2.1)对象消失:标记过的对象,引用了没有被标记的对象,例如引用链A->B->C->D,当GC标记完B,还没有标记C对象的时候,恰好应用程序将A引用D,C与D断开,此时,D将无法被标记
(2.2)浮动垃圾:假设程序原本C引用D,GC标记完之后,C断开D的引用,D成了新的垃圾
(3)重新标记阶段,会STW,该阶段会修正上一个阶段产生的对象消失这种情况
(3.1)修正对象消失:当对上个阶段对A进行增加引用的时候,如果A已经被标记过,那么将记录A(使用卡表),本阶段将A作为Root根,重新标记一边,以此方式来修正对象消失,也就是说上个阶段修改了对象引用链,那么这个阶段就要重新标记一下这段链
笔记:就是因为2和3两个阶段,导致CMS垃圾收集器只用于老年代,因为相对来说,老年代对象比较稳定,修正错误的情况相对较少,如果年轻代使用CMS,那么得一直修正错误
(4)并发清理阶段,不会STW,清除所有没被标记的对象

CMS的缺点

缺点1浮动垃圾下次GC的时候会清除,个人不认为这是缺点
缺点2会占用CPU资源,个人不认为这是缺点
缺点3默认会产生内存碎片,但是可以通过-XX:+UseCMSCompactAtFullCollection参数让jvm执行完毕之后进行一次压缩(整理),这样就不会产生碎片,但是会耗时,这是需要权衡的

CMS转换Serial(重要)
当CMS的2和4阶段进行时,如果产生full GC(比如老年代满了),则直接STW,之后,JVM会使用Serial垃圾收集器进行回收垃圾,因为这个过程很麻烦,所以采用串行是最简单的方式
笔记:这种情况也叫concurrent mode failure

下面的命令是CMS垃圾收集器常用命令,足够用

//在老年代使用CMS收集器,且该收集器只能用于老年代,不能用于年轻代
-XX:+UseConcMarkSweepGC
// 并发的GC线程数
-XX:ConcGCThreads
// GC之后进行压缩整理
-XX:+UseCMSCompactAtFullCollection
// GC多少次之后再压缩,默认0,表示每次GC都压缩带上
// 注意使用该参数时必须带-XX:+UseCMSCompactAtFullCollection参数
-XX:CMSFullGCsBeforeCompaction
// 当老年代对象第一次占老年代整个区域百分之多少的时候再回收
// 默认92,表示百分之92
-XX:CMSInitiatingOccupancyFraction

笔记:第一次会按照我们设定的这个值,之后GC的时候,不一定是按照我们设定的这个值,因为JVM内部会做一些优化,从而导致在我们设定的这个值上有浮动,比如我设定70,那么第一次达到70的时候会GC,但是第二次GC的时候不一定是70,有可能80,也有可能90,如果想要每次都按照70,则使用下面这个参数

// 需要配合上面的命令
-XX:+UseCMSInitatingOccupancyOnly

笔记:XX:CMSInitiatingOccupancyFraction参数越小,则表示GC时间越短,但是GC越频繁,这是需要权衡的

// 在full GC之前进行一次minor GC,目的是减少老年代对年轻代的引用
-XX:+CMSScavengeBeforeRemark

笔记:这个参数我持怀疑态度,按理说无论怎么minor GC,都不影响老年代对年轻代的引用,比如老年代有1个对象,引用了年轻代10个对象,无论如何minor GC,只要老年代这个对象存在,则年轻代这10个不是依然存在吗??而且是顺着老年代对象链查找的,也不会减少轮询次数,所以这个参数是我理解错了???

5.G1

与CMS不同,G1即可回收年轻代,又可回收老年代,而CMS只能用于老年代

G1将堆话分成多个大小相等的独立区域Region,最多可有2048个Region,
假设给堆分配4096M内存,则每个region占2M,下面的例子都会认为regison是2M
可以用-XX:G1HeapRegionSize指定每个Region大小,但是不推荐自己指定

region有四种类型,每个region如果对象清空了,那么这个regison下次有可能是任意类型

(1)Eden:eden+surivor默认占比整个堆的5%,但是如果满了可能会触发GC(Young GC),也可能会拓容,最多能拓容到60%,每次回收之前都会计算时间成本,如果时间成本特别低,则拓容,如果时间成本接近筛选回收的值,则GC
(2)Survior:和Ende的比例同之前一样,也是1:8
(3)Old:老年代
(4)Humongous大对象区,如果一个对象超过了regison大小的50%,本例中是1M,那么就是大对象,但是如果
分配的对象是5M,那么G1就会使用连续的三个Humongous来存放这个5M的对象,并没有放到老年代
(5)还有一些空region区

GC阶段

(1)初始标记:与CMS相同
(2)并发标记:与CMS相同,同时估算回收成本的时间权重,后续回收的时候会根据这个时间权重来排序
(3)最终标记:与CMS重新标记相同
(4)筛选回收:首先对每个Region的回收价值和成本进行排序(RSet活跃度,默认百分之85,-XX:G1MixedGCLiveThresholdPercent,超过该活跃度会被回收),根据用户指定的GC回收时间来回收,默认是200ms
可以通过-XX:MaxGCPauseMillis来指定回收持续多长时间

G1采用的算法以及优化:

G1采用复制算法,那么就会出现多个region合并的情况,比方说region1和regison2中GC之后总大小是1.5M,那么G1会将region1和regison2存活的对象都复制到同一个region中,然后清空regison1和regison2

G1独有的相关参数
-XX:InitiatingHeapOccupancyPercent
老年代占用的regison数量达到百分之多少(默认45%),就触发混合收集(MixedGC),混合收集指的是收集所有的Young区域,部分Old区域,部分Humongous区域,"部分"两个字由筛选回收阶段决定,注意,G1不到万不得已是不会触发Full GC的,这也是G1的初衷
-XX:G1MixedGCLiveThresholdPercent
每个region中存活的对象低于多少的时候,才会回收该region,注意是每个region中
-XX:G1HeapWastePercent
如果空出来的region达到百分之多少,就停止MixedGC,默认5%,优先级高于MaxGCPauseMillis

G1的Full GC
当MixedGC的复制算法没有region可用的时候,会出发full gc,原理同CMS,直接STW,开启Serial垃圾收集器开始单线程收集,非常耗时

G1使用场景
50%以上的堆都是存活的对象:此时如果其他收集器,存活对象多,则说明老年代对象就多,侧面说明老年代容易满,进一步导致频繁出发full GC
垃圾回收时间较长:如果使用其他收集器,回收时间比如超过1秒,那么体验就很不好,而G1的Mixed GC恰好可以指定时间
对STW停顿时间有要求的场景:原理同上
建议8G以上的堆内存:对于大内存,适合用G1,因为大内存满了之后其他收集器full GC的时候很消耗时间,而G1可以回收一会,运行一会,回收一会运行一会,体验更好

Java-虚拟机-垃圾收集器/垃圾收集算法/GCROOT根相关推荐

  1. Java虚拟机规范阅读(二)IEEE754简介以及Java虚拟机中的浮点算法

    什么是浮点数 在计算机系统的发展过程中,曾经提出过多种方法表达实数.典型的比如相对于浮点数的定点数(Fixed Point Number).在这种表达方式中,小数点固定的位于实数所有数字中间的某个位置 ...

  2. JVM 垃圾收集器 学习笔记(《深入理解java虚拟机》之六 垃圾收集)

    目录 新生代收集器 Serial收集器 ParNew收集器 Parallel Scavenge收集器 老年代收集器 Serial Old收集器 Parallel Old收集器 CMS收集器 Remov ...

  3. 深入理解Java虚拟机—低延迟垃圾收集器

    上一篇:深入理解Java虚拟机-垃圾收集器 下一篇:深入理解Java虚拟机-垃圾收集器适用场景 这一篇讲的是低延迟垃圾收集器 衡量垃圾收集器的三项最重要的指标是:内存占用(Footprint).吞吐量 ...

  4. 【Java 虚拟机原理】垃圾收集器 ( Serial | ParNew | Parallel Scavenge | CMS | Serial Old - MSC | Parallel Old )

    文章目录 前言 一.HotSpot 虚拟机的垃圾收集器 二.年轻代垃圾收集器 1. 串行收集器 ( Serial ) 2. ParNew 收集器 3. Parallel Scavenge 收集器 二. ...

  5. JVM 第二篇:垃圾收集器以及算法

    本文内容过于硬核,建议有 Java 相关经验人士阅读. 0. 引言 一说到 JVM ,大多数人第一个想到的可能就是 GC ,今天我们就来聊一聊和 GC 关系最大的垃圾收集器以及垃圾收集算法,希望能通过 ...

  6. Java虚拟机-第二篇-GC算法与内存分配策略

    2019独角兽企业重金招聘Python工程师标准>>> GC引入 在Java的运行时数据区中,程序计数器.虚拟机栈.本地方法栈三个区域都是线程私有的,随线程而生,随线程而灭,在方法结 ...

  7. Java虚拟机详解04----GC算法和种类【重要】

    本文主要内容: GC的概念 GC算法  引用计数法(无法解决循环引用的问题,不被java采纳) 根搜索算法 现代虚拟机中的垃圾搜集算法: 标记-清除 复制算法(新生代) 标记-压缩(老年代) 分代收集 ...

  8. Java虚拟机详解04----GC算法和种类

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  9. java虚拟机收集器_Java虚拟机(JVM)垃圾回收器G1收集器 - Break易站

    G1收集器 G1(Garbage-First)是JDK7-u4才推出商用的收集器: 1.特点 (A).并行与并发 能充分利用多CPU.多核环境下的硬件优势: 可以并行来缩短"Stop The ...

最新文章

  1. 话说TP框架里的Vendor这目录是干什么用的啊?类库扩展thinkphp3.1版本
  2. 变分贝叶斯variable bayes 和EM算法关系
  3. 20150920 DNS服务
  4. python写一个游戏多少代码-使用50行Python代码从零开始实现一个AI平衡小游戏
  5. PAT A1063——set的常见用法详解
  6. stm32 iic接口 进入busy_STM32通过IIC接口读取JY61模块MPU6050陀螺仪芯片数据核心程序...
  7. JavaScript——max-age
  8. json数据交换的例了
  9. 查询排序_Mysql在排序和查询时不使用索引的情况
  10. Leaflet文档阅读笔记-Leaflet on Mobile笔记
  11. 2台主机的docker互相通信的方法
  12. 《算法导论》第二章 入门
  13. [翻译]两大因素推动向上比特币价格上涨
  14. XP不能访问Win7共享
  15. MySQL知识总结 (六) MySQL调优
  16. python 微信群发_用python写一个微信群发工具(基于itchat库)
  17. 华为防火墙笔记-报文处理流程
  18. mysql 1044 42000_[故障解决]Mysql爆出ERROR 1044 (42000)的错误怎么办?
  19. Android->Launcher3:桌面布局说明
  20. NC WebService开发参考

热门文章

  1. 同步类容器与并发类容器
  2. Python中的for in if 用法
  3. MultipartFile与File之间的相互转换
  4. mysql 事务日志备份_SQL Server恢复模式与事务日志备份
  5. ftp ---- 本地用户登录(实例配置1:)
  6. asp.net三层架构制作新闻管理_程序员蜕变为架构师必须要知道的「架构理论」...
  7. 现代测试技术与软件关系大吗,现代测试技术及应用学习心得
  8. git命令提交本地代码到远程仓库
  9. centos7.8离线安装gcc
  10. rabbitmq页面出现/etc/rabbitmq/rabbitmq.config(not found)解决方法