引言

jvm垃圾回收相关的问题是老生常谈的问题了,相信大家都有所了解,这里再进行相关的探讨,以加深理解。若文中有不正之言,望不吝指正。

本文将围绕以下几个点展开

1.为什么要进行垃圾回收

我们知道jvm的内存结构中,依赖内存是否可共享,将内存划分为线程专享区和线程共享区,其中程序计数器、虚拟机栈、本地方法栈作为线程独享的内存,生命周期跟线程相关,比如栈中的栈帧需要分配多少内存一般在类结构确定时就已经确定好了,线程结束时内存就跟着回收了,不需要我们过多的关心垃圾回收。但是作为线程共享的内存区域,堆和方法区,java new出来的对象大部分是在堆中分配的,而且只有在程序运行期间才能知道会创建出哪些对象,在这部分内存的分配和回收是我们关心的。其实归根结底就是一句话,知道底层的jvm怎么进行垃圾回收的,对我们排查内存溢出和泄露的问题有很大帮助,而且对于提高程序的性能也有很大帮助。
复制代码

2.哪些内存需要回收

 判断哪些内存需要回收有两种经典的算法1.引用计数器法引用计数器法的实现是给对象添加一个引用计数器,当对象被引用时,计数器值就+1,对象引用失效时计数器值-1,当引用计数器值为0时,可认为对象无引用,此时可被回收。但是这个算法不能解决对象循环引用的问题,所以虚拟机并不是采用这种方式去判断是否回收对象的。2.可达性分析算法可达性分析算法是将可作为GC Roots的对象作为起始点,展开搜索,搜索所经过的路径,对一个对象到GC Roots 没有任何引用,则意味着对象不可达,此时对象可被回收。
复制代码

其中的GCRoots,其实就是一组必须活跃的引用,大致包括以下几种

  1. 虚拟机栈中引用的对象
  2. 方法区中静态属性引用的变量
  3. 方法区中常量引用的对象
  4. 本地方法栈中引用的对象

方法区垃圾回收:其实方法区中也是可以进行垃圾回收的,只是回收的性价比很低,就是说执行一次垃圾回收的动作并不能回收到很多的空间。但是满足一定条件还是可以进行回收的,一般回收的是无用的类和废弃常量。其中判断常量是否废弃,没有引用即可;而判断类无用需满足 1.java堆中没有任何该类的实例;2.加载该类的类加载器已被回收;3.该类的java.lang.Class对象没有在任何地方被引用 ,不能通过反射获取该类的方法。

3.何时进行垃圾回收

 即触发GC的时间,在新生代的Eden区满了,会触发新生代GC(MinorGC),经过多次触发新生代GC存活下来的对象就会升级到老年代,升级到老年代的对象所需的内存大于老年代剩余的内存,则会触发老年代GC(FullGC)。当程序调用System.gc()时也会触发Full GC。
复制代码

4. 垃圾收集算法

 1. 标记清除算法见名知意,该算法分为两个阶段,标记和清除。如下图所示,标记就是根据之前的GCRoots判断对象是否还有引用可达。但是这个算法的缺点也很明显,第一就是标记和清除效率都很低;第二就是这种算法会造成大量不连续的内存碎片的存在,当程序需要分配大的内存空间给对象的时候,很可能因为无法找到连续的大的内存空间,再一次触发GC。图如下:
复制代码

图中对象b没有引用,会被回收 图中可以看出,回收后有大量不连续的内存空间。

 2.复制算法基于上一个算法,为了解决效率问题,引申出“复制”算法。基本思想就是将内存划分为大小相等的两块,每次只使用其中的一块,当一块用完将整个活着的对象全部复制到另一个半区,此时不需要考虑内存碎片的问题,只需移动指针即可,简单高效。但是需要浪费一半的内存。由于java堆内存对象存活的特点,大部分新生代中的对象存活时间都比较短,所以主流的虚拟机会将内存分为一块较大的Eden区和两块较小的Survivor区,比例是8:1:1。每次分配对象到Eden区和一个Suivivor区,垃圾回收时将整个Eden区和刚才用到的Survivor区存活的对象一次性“复制”到未被使用的那个Survivor区,最后清理掉Eden区和之前的分配的Survivor区。但是并不能保证在将Eden去和Survivor区复制到另一个Survivor区的时候内存空间一定是充足的,此时需要依赖其它内存进行分配担保。分配担保策略指明,当Eden区和之前的Survivor区之存活的对象复制到另一块Survivor区时,内存空间若不够,则直接将内存分配到老年代。
复制代码

复制的时候只需移动相应的指针即可

 3.标记整理算法从上面的“复制”算法可以得知,当对象的存活率很高时,进行复制操作的时候效率将会变低。这时有人提出标记整理算法,标记过程较之前相同,只是后面不直接对对象进行清除,而是将存活的对象都向一边移动,并更改对应的指针,然后清理掉右端以外的内存。但是这个算法效率也不高因为,因为包括了标记 + 移动,但是也解决了内存碎片的问题。
复制代码

4.分代收集算法

    其实就是上面算法的综合。根据对象的存活周期,一般把java堆分为新生代和老生代。新生代中对象存活率较低,选用复制算法,只需付出少量存活对象的复制成本即可完成收集;老年代中对象存活率高,没有额外的内控空间进行担保,必须使用标记清除或者标记整理算法来进行垃圾回收。
复制代码

5.几种垃圾收集器比较

在进行比较之前先介绍下衡量的参数

  1. 吞吐量: 所谓吞吐量就是 CPU 用于运行代码的时间与 CPU 总消耗时间的比值,即吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)

  2. 停顿时间 :jvm在运行垃圾回收时,需停顿用户程序,这里指的是停顿用户程序的时常

Serial收集器

单线程的收集器,采用 复制算法,进行垃圾回收时需要将用户的所有线程全部暂停直到垃圾回收动作的完成。交互体验不是很好,但是在单核cpu或者虚拟机运行在client模式下,停顿时间可以控制到毫秒级,是可以接受。
复制代码

Serial Old收集器

 Serial Old是Serial的老年代版本,使用   标记整理算法
复制代码

ParNew收集器

 ParNew收集器采用复制算法,其实就是Serial收集器的多线程版本,是server模式下首选的新生代收集器。在多核cpu下有着比Serial更好的表现。
复制代码

Parallel Scavenge收集器

 采用复制算法,也是一个新生代的收集器。较于ParNew收集器,Parallel Scavenge收集器更多的目标是达到一个可控制的吞吐量。Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量,分别是控制最大垃圾收集 停顿时间的-XX:MaxGCPauseMillis参数以及直接设置吞吐量大小的-XX:GCTimeRatio参 数
复制代码

Parallel Old收集器

 Parallel Old收集器采用  标记整理算法 是ParallelScavenge收集器的老年代版本。在注重吞吐量以及cpu资源敏感的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器。
复制代码

CMS(Concurrent Mark Sweep)收集器

 采用标记清除算法,是一个老年代的并行收集器,是一个以获取最短回收停顿时间为目标的处理器,具有高并发、低停顿的特点。包括四个过程:初始标记、并发标记、重新标记、并发清除。其中初始标记和重新标记都需要暂停用户所有线程。
复制代码

但是这个收集器也有明显的缺点, 1. 前面介绍过的标记清除算法讲到会产生大量的内存碎片,很可能老年代空间有富余,但是新生代没有足够大的内存空间分配对象。CMS收集器提供了一个-XX:+UseCMSCompactAtFullCollection开 关参数(默认就是开启的),用于在CMS收集器顶不住要进行FullGC时开启内存碎片的合并 整理过程,内存整理的过程是无法并发的,空间碎片问题没有了,但停顿时间不得不变长; 2.CMS收集器无法处理浮动垃圾(由于cms并发清理阶段用户线程还在运行,这段时间内出现的新的垃圾称之为浮动垃圾) 3.cms对cpu的资源很敏感,虽然收集器是并发的,但是因为占用了一部分线程会导致应用程序变慢,总吞吐量降低。

G1收集器

G1收集器基于 标记整理算法 是面向服务端的,是面向新生代和老生代的收集器。收集过程主要包括:1.初始标记:标记GCRoots能关联的对象;
2.并发标记:从GC Roots开始对对象进行可达性分析,找出存活的对象,耗时较久,可与用户线程同时进行 3.最终标记:修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录 4.筛选回收:首先对各个region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,优先处理最需要回收的。
复制代码

收集器参数总结

引用

《深入理解java虚拟机》 周志明

转载于:https://juejin.im/post/5b2efb4451882574cf66a837

java学习笔记-4 JVM垃圾回收(GC)相关推荐

  1. 读书笔记之JVM垃圾回收

    前言 ​ 如果要问Java与其他编程语言最大的不同是什么,我第一个想到的一定就是Java所运行的JVM所自带的自动垃圾回收机制,以下是我学习JVM垃圾回收机制整理的笔记,希望能对读者有一些帮助. 哪些 ...

  2. jvm - 垃圾回收 gc

    2019独角兽企业重金招聘Python工程师标准>>> jvm - 垃圾回收 注意 : 本系列文章为学习系列,部分内容会取自相关书籍或者网络资源,在文章中间和末尾处会有标注 垃圾回收 ...

  3. Java教程分享:JVM垃圾回收机制之对象回收算法

    前言 在前面的文章中,介绍了JVM内存模型分为:堆区.虚拟机栈.方法区.本地方法区和程序计数器,其中堆区是JVM中最大的一块内存区域,在Java中的所有对象实例都保存在此区域,它能被所有线程共享. 在 ...

  4. Java学习笔记12——JVM入门

    文章目录 JVM简介和体系结构 JVM的位置 JVM的体系结构 类加载器 双亲委派机制 沙箱安全机制(了解即可) Native PC寄存器 方法区 栈 三种JVM 堆 新生区 永久区 堆内存调优 Jp ...

  5. 进阶学习(4.3) JVM 垃圾回收算法

    1, 引用计数法 Object  a = new Object(),  引用计数为0 Object  b =  a;   计数 +1 a = null  计数为0,  对象将被回收 b 也是垃圾对象 ...

  6. JVM垃圾回收(GC)

    如何识别垃圾 垃圾回收主要方法 分代收集算法 垃圾收集器 JVM参数 测试 如何识别垃圾 引用计数法 对象被引用一次,在它的对象头上加一次引用次数,如果没有被引用(引用次数为 0),则此对象可回收 代 ...

  7. NET学习笔记-3:垃圾回收与内存管理

    你清楚.Net的垃圾回收机制吗? 你能简述一下GC的工作原理吗? 怎么样才能有效的管理内存呢? Using语句体内实例化的对象有什么作用?等等相关问题. 本节的组织如下: 1..Net的类型和内存分配 ...

  8. JVM—垃圾回收GC算法

    1 GC算法简介 算法 特点 标记-清除 分为"标记"和"清除"两个阶段 复制 可以解决效率问题,将可用的内存按容量划分为大小相等的两块. 标记-整理 先标记. ...

  9. 4、JVM垃圾回收机制、新生代的GC、GC(Minor GC、FullGC)、GC日志、JVM参数选项、元空间(笔记)

    4.JVM垃圾回收机制 4.1.新生代的GC 4.1.1.串行GC(SerialGC) 4.1.2.并行回收GC(Parallel Scavenge) 4.1.3.并行GC(ParNew) 4.2.G ...

最新文章

  1. pmp考试用计算机,PMP是计算机类考试吗
  2. (转)Spring AOP的底层实现技术
  3. 在学习Python中,这个知识我们一定要看一遍,记不住没关系单一定要知道,字符串常用函数用法
  4. 关于字节序(大端法、小端法)的定义
  5. spotify 数据分析_我的Spotify流历史分析
  6. 29岁学python的建议_学习Python的建议
  7. 拳王虚拟项目公社:解除网站禁止复制的插件,Simple Allow Copy V 0.8.2
  8. 【长文综述】基于图神经网络的知识图谱研究进展
  9. Java常见面试题收集
  10. 黑马程序员-Java基础,Java集合Collection和Iterator接口
  11. Google浏览器下载地址
  12. 第17章:使用 concurrent.futures 模块处理并发-使用 futures.as_completed 函数立刻获取多线程任务执行结果
  13. vue中使用萤石云播放视频
  14. 实现“快准狠”交付——小米HR研发团队敏捷实践
  15. Project中最常用的注意点
  16. coreldraw x4怎么写字_CorelDRAW实例:给文字排版的几个小技巧(4)
  17. 如何用微博推广淘宝店铺
  18. 关于scene understanding场景理解概念的理解
  19. 【浅墨著作】 逐梦旅程 Windows游戏编程之从零开始 勘误 配套源代码下载
  20. 并联谐振电路频率阻抗及计算曲线带宽案例摘要

热门文章

  1. C#中格式化小数位数为指定位数的工具类
  2. 软考-信息系统项目管理师-项目成本管理
  3. 【Python】编程笔记10
  4. oracle自治事务的写法_Oracle的自治事务
  5. centos 对已有卷扩容_CentOS LVM 新加硬盘,扩容逻辑卷步骤
  6. LILY 英语携手神策数据 数据赋能少儿英语学习创新体验
  7. 神策数据荣获“2017年度中国双创好项目”奖项
  8. 一个9年的技术最近一直担心找不到工作
  9. ECharts概念学习系列之ECharts是什么?
  10. 排序箭头,升序,降序简单实现