暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

【科普】JVM的垃圾回收机制(GC)

达摩院首座 2019-04-18
495

      任何一个编程语言都需要内存资源来放置各种变量或缓存API调用对象。例如在C语言中,通过malloc(),realloc()或calloc()命令可以为程序提前保留内存资源,而在程序段结束后,通过free()命令可以释放保留,以防内存溢出。

        不同与C和C++,Java有自动的内存管理机制,这个机制就是GC,GC的工作原理是基于“大多数对象很快失联"以及”需要从‘老’对象返回‘新’对象重新建立关联的只有很小一部分”这两个假设。关于GC,我们需要知道:

  1. 对象创建在Java内存模型的堆(Heap)中,而静态成员和类定义(元数据)存放在“方法区域(Permgen)”


  2. GC的守护进程是”Garbage Collector“;

  3. GC不能强制启动或终止;

  4. 如果内存堆(Heap)满了而无法分配新的对象时,你会收到java.lang.OutOfMemoryError的报错。

       

      一个完整的垃圾回收周期会有三个阶段:

  1. 标记阶段:遍历对象的调用关系,从叶节点到根节点,将可达节点/对象标记为live;

  2. 清理阶段:删除不可达节点/对象;

  3. 压缩阶段:移动剩下的节点/对象使其在内存中的空间是连续的。

        Java的内存堆分为年轻代(Young Generation)和年老代(Old Generation)两部分,新的对象存放在年轻代的Eden空间中(就像圣经中的Eden园是人类起源的地方),当Eden空间被填满时,GC进程会自动清理掉不可达对象,然后将可达对象移动到Survivor空间中,以确保新创建的对象可以使用Eden空间。下一个GC周期清理后,如果Survivor对象依然可达,GC会将其转存到另一个Survivor空间。若干次GC之后,这些可达对象会被转存到年老代中,以表示这些对象是覆盖应用全生命周期的。只在年轻代中移动对象的GC进程称作Minor GC,在年轻代和年老代间移动对象的GC进程称作Major GC

      我们来具体看下一个Minor GC的整个过程,当Eden满了时候,Minor GC将其可达对象放入Survivor中。

       然后清理掉Eden中的不可达对象,也就是把整个Eden空间清空。在下一次Eden空间被占满时,Minor GC会再一次标记可达和不可达对象(包括在Survivor1中的对象),然后将所有可达对象移动到Survivor2中

然后清理掉Survivor1和Eden空间中的不可达对象,继续给剩余的Survivor打标签以标记其存活的轮次。

       反复移动的目的是为了使这些可达对象在内存中是连续的,以提高调用效率。

       下一个周期中,Eden和Survivor2中的存活对象会被移动到Survivor1中,然后更新存活周期标签。

        Minor GC什么时候到头呢?通过JVM定义的MaxTenuringThreshold将决定存活周期的阈值。比如说16,那在Minor GC中腾挪超过16次依然可达的对象将被提升到(Promotion)年老代中。

        那么问题来了,当年老代也占满了的时候,怎么保障内存不溢出呢?在年老代接近满载的时候,Major GC会发挥作用,依次执行我们前面所说的标记、清理和压缩,这时如果你的堆空间非常大,比如有16GB,这个过程将非常耗时,往往会造成应用hang住。

        今天说的有点多,关于这个话题,我们下一节课会讨论如何避免应用hang住及提升应用的表现,感谢您的关注!

达摩院首座∣一起开源一起嗨



长按,识别二维码,加关注


文章转载自达摩院首座,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论