GC(Garbage Collection)垃圾回收,释放垃圾占用的空间,对堆中已经死亡或者长时间没有使用的对象进行清除和回收,防止内存泄漏。可以有效使用内存空间。
垃圾收集之前需要定义什么是垃圾,之后才能决定如何回收垃圾。
抛开书面上介绍的几种垃圾分析算法,一步到位说下jvm采用的可达性分析法。
基本思路是通过根引用(GC ROOT)作为分析起点,沿着节点向下搜索,搜索路径称为引用链(Reference Chain),当一个对象到GC ROOT没有任何引用链时,证明对象不再使用(GC ROOT到对象节点不可达)。
通过可达性分析算法可以解决引用计数无法解决的“循环依赖”,只要对象和GC ROOT之间无法建立直接或者间接的链接,就可以认定为可回收对象。
既然GC ROOT在对象可达性分析时如此重要,那么哪些对象可以作为GC ROOT呢?
在之前需要先了解下JVM的内部结构:
以下四种情况可以认定为GC ROOT:
在确定垃圾之后就可以进行垃圾回收了,回收过程中最重要的一点就是如何高效的回收,这些也都是不同版本回收器进化的主要目标。
常见的垃圾收集算法:
标记清除
标记清除(Mark-Sweep)是将内存中认定为垃圾的对象进行标记,之后对这些标记的对象进行清理,清理之后的空间可以给新对象使用。
但是这种操作算法存在的问题会存在大量不连续的清理空间,也就是内存碎片。内存碎片带来的问题是,有的时候我们进行对象分配时,需要连续的内存(比如数组这种)但是由于内存中没有足够的联系内存,导致碎片内存用不了,造成了内存空间的浪费。
复制算法
复制(Copying)是在标记清除之上演进而来的,主要目的是解决内存碎片问题,将可用内存空间按照容量进行划分成大小相等的两块,每次只是用其中一块,当这部分内存使用完成之后,可以把存活对象复制到另一块上面,把之前使用的内存进行一次性清理,保证内存空间连续可用,复制算法我们可以不考虑内存碎片等问题,分配过程更简单高效。
但是复制算法带来的就是空间的代价更大。
标记整理
既然标记清除,复制算法都存在一些明确的短板,是否可以针对这些短板设计一个回收算法呢?于是就有了标记整理算法(Mark-Compact)。
标记整理过程和标记清除算法一样,但是后续整理方式是将存活对象统一移动到内存到一侧,在进行边界外的对象清除。
这种算法属于标记清除算法的升级版本,可以解决内存碎片问题,也避免了复制算法存在一半空间浪费的问题,但是也不是没有问题。首先他会造成更多的内存变动,比如需要判断存活对象,整理存活对象地址,效率上对于复制算法来说要差很多。
我们了解了三种收集算法,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参数一个很重要的参考点),所以内存并不是越大越好。上面说了老年代存在大量长期对象,所以采用标记算法更合适。
哪些对象会进入老年代?