JVM垃圾回收笔记

聊聊GC

在JVM运行中会不断的产生对象,而产生对象就会有对象死亡,这时候就需要GC垃圾回收器来收集.

GC垃圾回收分为四个阶段

1.初始标记(STW)

2.并发标记

3.重新标记(STW)

4.清理垃圾(STW)

因为GC先进行可达性分析。可达性分析是判断GC Root对象到其他对象是否可达,假如分析过程中对象的引用关系在不断变化,分析结果的准确性就无法得到保证。

如果进行标记的时候线程一直运行标记永远不能结束,所以行初始标记和重新标记还有垃圾清理的时候会触发STW(世界暂停).当前用户线程会被暂停,转而进行我们的垃圾回收线程.

JVM机会对当前线程中存活的对象进行标记,判断对象是否存活.

第一种是引用计数法:给每个对象都添加一个引用计数器,当对象被引用一次就+1,引用失效一次就-1,当为0的时候就是垃圾对象;但是会有一个相互循环引用的问题

第二种可达性分析法:通过GCroot根找到直接节点向下寻找,被找到的可到达对象都是会被标记成存活对象.

直接节点可以是:

虚拟机栈中引用的对象
方法区常量或类静态属性引用的对象
本地方法栈中JNI的引用对象

当JVM机在进行并发标记的时候,可能会产生浮动垃圾或者对象消失.

浮动垃圾是当对象被标记为存活对象后变成了垃圾,不会被清理,但是可以等到下一轮垃圾回收进行清理,问题不是很大.

而对象消失是可达性分析的时候它确实是垃圾,但是分析完毕后,又被其他线程抢救了,不再是垃圾了,但是没有被标记,所以会被回收.

对象消失是不能允许的,否则线程会出现错误或者异常,而JVM机使用GC屏障来进行处理.

GC屏障就是在进行对象操作的时候进行一些操作,将对象的变动记录下来(类似AOP原理),当有新的对象被引用时,会被保存下来,而不会被回收,当并发扫描结束后,将记录的结果进行重新标记,这样就可以保证对象不会消失.

以上说的是标记部分,既然标记完了就应该回收,当重新标记结束后就会开始垃圾清理.

进行垃圾回收的时候,会使用垃圾回收算法,分别为:

标记-清理:将存活的对象标记,然后清理没标记的垃圾,这样清理很简单,但是会产生内存碎片;

复制算法:将内存分成两块一样大小的内存,当进行垃圾清理时,将存活对象复制到未被使用的另一块内存上,然后清空当前使用的内存,这样做简单高效,而且不会出现内存碎片,但是对内存占用很高.

标记-压缩法:将所被标记存活的对象压缩到内存的一端。之后,清理边界外所有的空间。解决了内存碎片问题,但是移动对象效率会被影响.

除了这三种还有一种就是通用的分代算法,也叫分代收集法.

而分带算法其实也可以算是对上面三种方法的应用

我们可以用三种假说来解释内存中的对象关系:

弱分代假说(Weak Generational Hypothesis):绝大多数对象都是朝生夕灭的,所以这部分对象创建和消亡很频繁,可以采用复制算法收集.
强分代假说(Strong Generational Hypothesis):熬过越多次垃圾收集过程的对象就越难以消亡,这种对象基本存活的很久,所以可以使用标记-压缩法回收.
跨代引用假说(Intergenerational Reference Hypothesis):跨代引用相对于同代引用来说仅占极少数,这个问题先放在这,后面聊到.

Java 虚拟机根据对象存活的周期不同,把堆内存划分为几块。

所以在上面这三种假说上,建立了年轻代,老年代两个区域,用来存放不同分代的存活对象,并且对两个区域用不同的方式收集垃圾.这就是JVM 的内存分代策略.

分代回收的中心思想就是:对于新创建的对象会在新生代中分配内存,此区域的对象生命周期一般较短。如果经过多次回收仍然存活下来,则将它们转移到老年代中。

年轻代还分为Eden区和Survivor区,Survivor区又分为From区(S0)和To区(S1),三个区域分别是8:1:1.

一般刚创建的对象都会放入Eden区,当 Eden 区第一次满的时候,会触发YongGC.进行YongGC前会进行GC担保,如果担保失败会判断以往的yongGC后的存活平均数如果大于老年代内存则进行FullGC,否则进行YongGC,而YongGC首先将 Eden区的垃圾对象回收清除,并将存活的对象年龄+1然后复制到 S0,此时 S1是空的,下一次 Eden 区满时,再执行一次垃圾回收。此次会将 Eden和 S0区中所有垃圾对象清除,并将存活对象年龄+1复制到 S1,此时 S0变为空。如此反复在 S0 和 S1之间切换,当对象年龄达到设定或者默认(15)年龄,如果还是存活对象,说明这些对象的生命周期较长,则将它们转移到老年代中。

老年代:一个对象如果在新生代到达年龄而没被清理,则会被复制到老年代。老年代的内存大小一般比新生代大,能存放更多的对象。如果对象比较大(比如长字符串或者大数组),并且新生代的剩余空间不足,则这个大对象会直接被分配到老年代上,而当老年代满了的时候会触发Full GC,会清理所有内存中的垃圾对象,。

注意:对于老年代可能存在这么一种情况,老年代中的对象有时候会引用到新生代对象。而GC屏障也会将这个记录下来,但是这时如果要执行新生代 GC,则可能需要查询整个老年代上可能存在引用新生代的情况,这显然是低效的。所以,将老年代分为若干个区域,每个区域的老年代中维护了一个 512 byte 的 card table也就是一个bitmap,所有老年代对象引用新生代对象的信息都记录在这里。每当新生代发生 GC 时,只需要检查这个 card table 即可,大大提高了性能。
 

以上就是垃圾回收的方法,既然有了回收方法就要有垃圾回收器来执行.

清理要用到垃圾收集器,垃圾收集器也分很多种:

1.串行垃圾收集器

它只能单线程工作,所以在运行的时候其他线程必须STW, 串行的垃圾收集器有两种,Serial与Serial Old,一般两者搭配使用。

2.G1回收器(默认使用)

G1回收器会将堆分成2048块大小相同的区域,每个区域可以是不同的类型,可以是年轻代,老年代,大对象区和空闲区这几种类型.

而大对象定义是占该区域的一半就算大对象.

而G1回收会使用Mix GC:这是G1独有的垃圾收集方式,指定时间干多少活.

比方指定100ms干活,那么只干100ms,不管干没干完.

如果干完了,可以继续多干点,但是如果干不完,则要权重收集垃圾,即尽可能的回收最大价值的垃圾对象.

权重的就是对象的活跃度,在进行并发标记的时候会筛选权重,对象集中的对象引用的活跃度,越活跃的对象越容易被回收.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值