美文网首页
JVM虚拟机系列----(三)垃圾回收机制

JVM虚拟机系列----(三)垃圾回收机制

作者: 梦飞成2012 | 来源:发表于2017-10-18 18:35 被阅读50次

Java 和C++之间有一堵由内存动态分配和垃圾收集收集技术围成的“高墙”,墙外面的人想进去,墙里面的人想出来。
                   ----周志明《深入理解Java虚拟机》

说起垃圾回收机制(Garbage Collection ,GC),大部分人都以为这项技术是伴随着Java语言产生的。
事实上,GC 比Java的历史久远的多,早在1960年MIT的Lisp是第一门使用内存动态分配和垃圾回收技术的语言。
当时的GC技术,人们就在思考三件事情:
  1、哪些内存需要回收?
  2、什么时候回收?
  3、如何回收?

一、内存回收的区域?

  • 程序计数器、本地方法栈、虚拟机栈:这几块内存都是随线程而生,随线程而灭。栈中的栈帧,随着方法的调用入栈和出栈,栈帧的内存大小在编译器就已经可知了,因此内存的分配和回收是确定的,故不需要参加GC。
  • :内存的回收的主力区域,对象和数组都保存在此区域。
  • 方法区 :无用的类型信息以及常量都是回收的目标,但是一般进行GC的概率不大,主要是性价比不高。

二、哪些内存需要回收?

要确定哪些内存需要回收,目前有两种常用的方案

  • 1、引用计数法:

     给对象添加一个引用计数器,当对象被引用时,计数器的值加1;当对象引用失效时,计数器的值减1,
     当计数器的值为0时,就可以回收了。
     这种方法实现简单,判定效率也很高。是一个不错的算法,微软的Com技术、Python、
     一些脚本语言都用此方法管理内存。
     但主流的Java语言却没有使用这种方式,主要原因是它无法解决对象的相互循环引用问题。
    

如下图所示:

循环引用.png
  • 2、可达性分析:

      (1)从GC Roots 作为起点,从这些节点开始向下搜索,搜索经过的链路称为引用链。不在引用链上的(图论中称为可达)都是无用的对象。
      (2)可作为GC Roots 的对象:
            JVM栈中引用的对象(栈帧的局部变量表的对象)
            类中的static引用、final引用
            本地方法栈中引用的对象
    

可达性分析对象是否可用,如下图:

可达性分析
  • 3、引用类型介绍:

      i、强引用:JVM即使抛出OOM也不会回收强引用指向的对象
      ii、软引用:描述一些还有用但是并非必须的对象,对于软引用,JVM会在内存溢出前进行第二次回收,如果这次回收还没有足够内存,才会抛出OOM。
      iii、弱引用:同样描述还有用但是非必须的对象,强度比软引用还弱,该引用指向的对象,只能存活到下次GC之前,只要发生GC,就会被回收。
      iv、虚引用:最弱的引用关系,没啥用,为对象设置虚引用关联的唯一目的就是,该对象被回收的时候会收到一个系统通知。
    
  • 4、finalize方法:
    对象自救的方法,建议不要使用,在《effective java》等书籍中都不推荐使用,具体原因,读者可以自行查阅。

三、如何进行回收?

  • 3.1 标记-清除算法
    原理:首先将无用的内存标记出来,然后统一释放。
    缺点:效率问题,标记和清除的两个过程效率都不高;同时回收后,将会产生大量不连续的内存,空间碎片太多甚至无法满足一个大对象的申请,可能导致提前进行下一次GC。
    效果图如下:
标记-清除回收算法示意图
  • 3.2 复制算法
    原理:将内存分为相等的两块,然后每次只使用其中的一块,当发生GC的时候,将有效的对象复制到另一块空白内存,然后把已使用的全部清理。
    优点:简单易行,运行高效,解决了内存碎片的问题。
    缺点:浪费了一半的内存空间
    PS:这种回收算法目前一般应用在堆的新生代回收,当然内存比例也不是1:1,后续在介绍新生代、老年代的时候,我们会再细讲。
    效果图如下:
复制回收算法示意图
  • 3.3 标记整理算法
    和标记-清除算法类似,只是在标记之后,不是清除无用对象,而是将所有存活对象,移动到一端,然后清理掉边界意外的内存区域。
    优缺点:这种算法避免内存空间的浪费,同时又解决了内存碎片问题。但是由于移动对象的效率问题,所以该回收算法,一般放在老年代进行。
    示意图如下:
标记整理算法示意图

JVM中的分代回收算法:

现代的JVM虚拟机都采用了分代回收,即分为新生代和老年代,新生代采用复制回收算法,老年代采用标记整理算法。
新生代:

  • new出来的对象一般都放在新生代
  • hotspot虚拟机新生代分为三块,Eden区、From Survivor、To Survivor区(其中两者相等),大小比例为8:1:1.
  • 之所以新生代采用复制算法,并且这样设计大小,是有依据的,IBM研究过新生代的对象98%的对象都熬不到下一次GC,即“朝生夕死”,所以这样设计效率很高。
  • 新生代发生GC(minor GC)的时候,会将Eden区存活的对象和Survivor中的对象,一起复制到另外一个Survivor中,然后清空Eden和Survivor区。
  • 新生代的对象可能在多次Minor GC后移到老年代。

老年代:
老年代的对象,一般存活时间较长,因此回收效率不高,性价比不高。
老年代的对象一般来自下面几种情况:

  • 1、经历多次minor GC未被回收的(一般是15次,在虚拟机中对象有个age属性,通常每经历一次minorGC,对象的age属性+1,当age大于15即放到老年第中)
  • 2、new的对象过大,而新生代经历了minor GC之后,依然放不下这个对象,那么这个对象将直接被放入到老年代。
  • 3、可通过启动参数设置-XX:PretenureSizeThreshold=1024(单位为字节,默认为0)来代表超过多大时就不在新生代分配,而是直接在老年代分配。
  • 4、如1中所述,新生代的某个age的对象,达到新生代Survivor空间一半以上时,大于或等于这个年里的这些对象将会被移到老年代中去。

新生代和老年代的内存图如下:

新生代和老年代

JVM中的GC类型:

上面讲到了新生代和老年代:
这两块区域对应的GC分别为Minor GC和Major GC,这两个GC一起触发叫做Full GC(当然也有资料称Major GC 就是Full GC)

  • Minor GC:新生代的GC,由于采用复制回收算法,所以速度很快。
  • Major GC:老年代的GC,一般发生时都会伴随一次Minor GC,由于采用标记整理算法,而且Major GC 一般是Minor GC的10倍以上。同时,由于老年代的对象,存活时间一般较长,因此回收的性价比不高。
    一般的,我们要尽量避免Major GC的发生,比如不要频繁申请大块内存,这样新生代放不下就放到了老年代,老年代内存回收,速度慢,就会导致系统卡顿,影响体验。

四、何时出发内存回收(GC)?

  • GC有JVM发起,不受应用控制。即使调用system.gc()
    方法也只是建议JVM进行一次GC,而不会立即进行系统的GC操作。
  • GC时机:
    新生代:Eden区放不下会进行一次新生代GC,即Minor GC。
    老年代:老年代的内存不够的时候,会触发一次老年代GC,即Major GC,一般都会伴随一次Minor GC。
  • JVM进行GC的时候,也不是随时随刻都可以进行的,一般要等到安全节点(safe point)或者安全区域(safe region)才会进行,以在特定的位置记录一些栈的信息,这一块比较复杂,就不在这里展开了。

五、垃圾回收器?

目前所有的垃圾回收器 都不能做到完全并行。至少在对象标记节点,都要暂停所有用户线程(“stop the world”)。
目前新生代的垃圾回收器有Serial、ParNew、Parallel Scavenge,老年代的回收器有Serial old、ParNew Old、CMS等。其中Serial 是单线程的,效率较低,后面的几个在一定程度上做到了并行,效率较高。垃圾回收器一般关注两个点吞吐量、暂停时间

  • 吞吐量:在一定时间内,非暂停时间占的比例,吞吐量高,说明暂停时间端,处理时间长,一般服务器比较关注这些。
  • 暂停时间:暂停的时间,就是GC导致的暂停时间,一般客户端应用比较关注这个。

六、JVM内存分配策略?

JVM提倡自动内存管理,最终归结为两个问题:自动分配内存以及自动回收内存。关于回收,上面我们已经将的很细致了,下面我们讲一下内存分配,这将对我们写更高效的代码提供帮助。

  • 1、对象优先分配在Eden区:这一点很容易理解
  • 2、大对象直接进入老年代:这个对象多大有具体参数设置,我们应该避免“朝生夕死”的大对象,比如大的数组,最糟糕的是一群大对象。当我们申请大对象的时候,即使虚拟机还有空间,也不得不提前GC以申请连续的空间来存放它们。
  • 3、长期存活的对象将进入老年代:这个我们前面讲过,默认age超过15 对象将进入老年代,所以如果对象无用了,我们应该及时释放其引用。
  • 4、新生代某一个年龄的对象,达到Survivor空间的一半时候,则年龄大于等于该年龄的对象将进入老年代。
  • 5、空间分配担保:老年代为了减少Major GC会进行空间分配担保,以节约时间,但是这个有一定风险,具体不在此展开讲了。

参考资料:

1、JVM垃圾回收
2、jvm:停止复制、标记清除、标记整理算法
3、JVM 垃圾回收 Minor gc vs Major gc vs Full gc
4、JVM学习03-内存管理和垃圾回收04(之GC算法 垃圾收集器)
5、Major GC和Full GC的区别是什么?触发条件呢?

相关文章

网友评论

      本文标题:JVM虚拟机系列----(三)垃圾回收机制

      本文链接:https://www.haomeiwen.com/subject/nzvsuxtx.html