专栏首页服务端技术杂谈GC和垃圾回收器其一

GC和垃圾回收器其一

什么是GC

GC(Garbage Collection)垃圾回收,释放垃圾占用的空间,对堆中已经死亡或者长时间没有使用的对象进行清除和回收,防止内存泄漏。可以有效使用内存空间。

什么是垃圾

垃圾收集之前需要定义什么是垃圾,之后才能决定如何回收垃圾。

抛开书面上介绍的几种垃圾分析算法,一步到位说下jvm采用的可达性分析法。

可达性分析

基本思路是通过根引用(GC ROOT)作为分析起点,沿着节点向下搜索,搜索路径称为引用链(Reference Chain),当一个对象到GC ROOT没有任何引用链时,证明对象不再使用(GC ROOT到对象节点不可达)。

通过可达性分析算法可以解决引用计数无法解决的“循环依赖”,只要对象和GC ROOT之间无法建立直接或者间接的链接,就可以认定为可回收对象。

GC ROOT

既然GC ROOT在对象可达性分析时如此重要,那么哪些对象可以作为GC ROOT呢?

在之前需要先了解下JVM的内部结构:

以下四种情况可以认定为GC ROOT:

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

垃圾回收

在确定垃圾之后就可以进行垃圾回收了,回收过程中最重要的一点就是如何高效的回收,这些也都是不同版本回收器进化的主要目标。

常见的垃圾收集算法:

  • 标记清除
  • 复制算法
  • 标记整理

标记清除

标记清除(Mark-Sweep)是将内存中认定为垃圾的对象进行标记,之后对这些标记的对象进行清理,清理之后的空间可以给新对象使用。

但是这种操作算法存在的问题会存在大量不连续的清理空间,也就是内存碎片。内存碎片带来的问题是,有的时候我们进行对象分配时,需要连续的内存(比如数组这种)但是由于内存中没有足够的联系内存,导致碎片内存用不了,造成了内存空间的浪费。

复制算法

复制(Copying)是在标记清除之上演进而来的,主要目的是解决内存碎片问题,将可用内存空间按照容量进行划分成大小相等的两块,每次只是用其中一块,当这部分内存使用完成之后,可以把存活对象复制到另一块上面,把之前使用的内存进行一次性清理,保证内存空间连续可用,复制算法我们可以不考虑内存碎片等问题,分配过程更简单高效。

但是复制算法带来的就是空间的代价更大。

标记整理

既然标记清除,复制算法都存在一些明确的短板,是否可以针对这些短板设计一个回收算法呢?于是就有了标记整理算法(Mark-Compact)。

标记整理过程和标记清除算法一样,但是后续整理方式是将存活对象统一移动到内存到一侧,在进行边界外的对象清除。

这种算法属于标记清除算法的升级版本,可以解决内存碎片问题,也避免了复制算法存在一半空间浪费的问题,但是也不是没有问题。首先他会造成更多的内存变动,比如需要判断存活对象,整理存活对象地址,效率上对于复制算法来说要差很多。

分代回收

我们了解了三种收集算法,JVM内存回收就可以根据自己特点进行算法选择了。

比如JVM根据对象存活周期不同将内存划分成不同的几部分。比如堆中针对于快生快消特点分出了新生代,存活较久对象分出了老年代。于是针对不同代对象特点可以选择不同的回收算法,比如年轻代对象生命周期比较短,可选择复制算法减少标记整理的代价。老年代存活对象较多,空间利用率上相对要求较高,需要使用标记清理或者标识整理算法。

JVM内存模型

那么JVM中对象具体是怎么分配的呢?从内存模型讲起。

堆是内存中最大的一块,也是垃圾回收的主要战场。

堆主要分为两个区域:年轻代和老年代,年轻代又分为Eden,s1,s2区。

Eden

新生对象优先在Eden中分配,当Eden中没有足够空间后,会触发一次YGC(比较讨厌用Minor GC和Major GC的说法),YGC之后Eden区被回收,无需回收的对象进入S1区,如果S1区容不下则直接进入老年代。

S区

S区主要作用是作为Eden和Old之间的缓冲带,因为之前说过Eden大部分对象都是短生命的,所以为了避免YGC之后直接进入老年代,引入S区也是有必要的,缓冲了因为频繁YGC使得老年代被填满的风险。

因为一而再再而三之后大部分对象还是在新生代消亡了,所以设置一个S区是明智的。

一般对象需要经过15(默认)YGC之后才进入老年代。

而两个S区的目的就是为了采用复制算法,来解决碎片问题。如果一个区域的话你就只能采取标记整理或标记清除算法了,为了降低YGC期间(因为YGC太频繁了)对于程序的影响,用复制算法这种简单可依赖的方式还有有必要的。

如果分成好几个S区会有什么问题呢?频繁的复制是个问题,过多的S区空间造成整体空间利用率更低也是个问题,相信这个2的阈值也是经过多次实验得来的,所以GC的很多默认算法其实是很优的配置,除非结合自己业务特点,否则比建议进行修改。

Old区

默认老年代占用整个区的2/3,只有发生Full GC(这个说法也不准确,我们用CMS回收器,所以就称为CMS GC吧)时才进行整理,Full GC会造成STW,内存越大STW肯定时间就越长(这个也是我们调优JVM参数一个很重要的参考点),所以内存并不是越大越好。上面说了老年代存在大量长期对象,所以采用标记算法更合适。

哪些对象会进入老年代?

  • 内存担保,无法放置对象直接进入老年代
  • 大对象直接进入老年代,可配置
  • 长期存活对象进入老年代,比如age=15
  • 动态对象年龄进入老年代,主要是s区空间不足了,某一个年龄及以上对象大小总和大于整个S区一半,这些年龄的对象直接进入老年代

本文分享自微信公众号 - 春哥叨叨(chungedaodao),作者:春哥大魔王

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-07-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 号外!号外!技术大牛撕逼了!竟然从CTO写不写代码开始?

    火热的八月已经过去,北京迎来了秋高气爽,社交圈子却一直火热,从宝宝的经纪人出轨了宝宝的宝宝,到从洪荒少女开始的卖萌奥运代表团,再到最近的技术大V撕逼大战,吃瓜观...

    春哥大魔王
  • GC和垃圾回收器其二

    CMS是并行标记回收器,使用标记-清除算法进行收集。适用于对时延要求较高的在线服务,不接受长时间停顿的那种。但是如果服务运行较长时间,会造成严重的内存碎片。

    春哥大魔王
  • JVM堆引发swap的情况分析

    虚拟机技术可以使得一个只有1g物理内存的机器可以运行总共需要4g内存的任务,主要方法是通过虚拟内存和物理内存映射来实现的,当物理内存不够用的时候,可以通过swa...

    春哥大魔王
  • JVM GC知识回顾 1.分代收集2.分区收集

    当前主流VM垃圾收集都采用”分代收集”(Generational Collection)算法, 这种算法会根据对象存活周期的不同将内存划分为几块, 如JVM中的...

    mantou
  • 直击面试,聊聊 GC 机制

    GC 中文直译垃圾回收,是一种回收内存空间避免内存泄漏的机制。当 JVM 内存紧张,通过执行 GC 有效回收内存,转而分配给新对象从而实现内存的再利用。 JVM...

    andyxh
  • JVM垃圾算法和GC三算法及面试题分享

    本期课程的内容概要:熟悉JVM架构与GC垃圾回收机制以及相应的堆参调优,有过在linux进系统调优的经验。

    艾编程
  • 面试官,不要再问我“Java GC垃圾回收机制”了

    在上篇《JVM之内存结构详解》中有些内容我们没有讲,本篇结合垃圾回收机制来一起学习。还记得JVM中堆的结构图吗?

    沉默王二
  • 面试官,不要再问我“Java GC垃圾回收机制”了

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    程序新视界
  • 深入理解Java中的Garbage Collection

    最近由于系统业务量比较大,从生产的GC日志(结合Pinpoint)来看,需要对部分系统进行GC调优。但是鉴于以往不是专门做这一块,但是一直都有零散的积累,这里做...

    Throwable
  • 深入理解 JVM 垃圾回收机制及其实现原理

    对于 JVM 来说,我们都不陌生,其是 Java Virtual Machine(Java 虚拟机)的缩写,它也是一个虚构出来的计算机,是通过在实际的计算机上仿...

    CG国斌

扫码关注云+社区

领取腾讯云代金券