Java 垃圾回收机制

本文主要围绕如下几个问题展开:

  1. 什么是堆内存
  2. 垃圾定义
  3. 回收的方案
  4. 分代回收垃圾机制

概述

垃圾回收机制,Java很是重要的特性之一,它让开发者无需关注空间的建立和释放,而是以守护进程的形式在后台自动回收垃圾。算法

堆内存

堆,是在JVM启动时建立的,属于JVM运行时数据区的重要组成部分,主要用来维护运行时数据,如运行过程当中建立的实例对象和数据。若是动态建立的对象没有获得及时回收,持续堆积,最后会致使堆空间被占满,形成内存溢出。设计

Java提供的垃圾回收机制,在后台建立一个守护进程。在内存紧张时自动执行垃圾回收,从而保证程序的正常运行。对象

垃圾定义

所谓“垃圾”,指全部再也不存活的对象进程

垃圾判断

常见判断对象是否存活的两种方法:图片

  • 引用计数法 为每个对象建立一个引用计数器,用来存储该对象被引用的个数。当计数为0时,意味着该对象再也不被使用,能够认为**“对象死亡”**。可是该方案存在严重的问题:没法检测“循环引用”,当两个对象相互引用时,即便他俩不被其余对象引用时,他俩的计数都不会为0,所以永远不会被回收。

实际上,Java里没有采用这样的方案来断定对象的“存活性”。内存

  • 可达性分析 目前主流的开发语言都采用对象存活性判断方案,基本思路是:把全部引用的对象想象成一颗树,从树的根节点(GC Roots)出发,持续遍历找出全部链接的树枝对象(包含叶子节点),这些对象成为“存活对象”或“可达对象”。其他的对象被成为“死亡对象”或“不可达对象”,亦或者“垃圾”。

参考下图,object五、object六、object7为不可达对象,视为“垃圾”,会被垃圾回收器回收 输入图片说明开发

GC Roots

GC Roots自己必定是可达的,从他们出发遍历到的对象才能保证必定可达。 Java里存在如下四种必定可达对象:虚拟机

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

垃圾回收方案

备注:下述图中,黑色表明垃圾对象灰色表明存活对象绿色表明空白空间it

标记-清理

第一步,标记,利用可达性遍历堆内存时,把存活对象和垃圾对象进行标记(以下图所示) 输入图片说明io

第二步,清理,把标记为垃圾对象进行清空,释放所占空间 输入图片说明

总结,该方案简单方便,但容易产生内存碎片。

标记-整理

基于标记-清理方案,在清理时将全部存活的对象集中到一块儿,造成一个连续使用的内存空间,以下图所示: 输入图片说明

总结,标记-清理、标记-整理两种方案,适合存活对象多、垃圾少的状况,只须要清理较少的垃圾,挪动一下存活对象就能够了。

复制

这种比较粗暴,将堆内存一分为二成两部分,一段时间内只容许在其中一块内存上进行分配,当该内存块被分配完后,则执行垃圾回收:把全部存活对象复制到另外一块内存里,而后直接清空当前内存。

输入图片说明

输入图片说明

总结,这种作法不易产生碎片,简单粗暴;可是,它只容许一段时间内只能使用一部份内存,超过这部份内存的话就有频繁的复制清空。这种方案适合存活对象少、垃圾多的状况,在复制只需移动少许的存活对象。

分代回收机制

上述讲到三种内存回收方案,那Java中是如何选择利用这三种回收算法呢?

堆结构

Java 堆空间(Heap)分红三部分,这三部分存储三类数据:

  • 新建立的对象,新生代区域,特色:存活对象少、垃圾多(复制)
  • 存活了一段时间的对象,老年代区域,特色:存活对象多、垃圾少(标记-整理)
  • 永久存在的对象,永久代区域,特色:永远存活,不须要垃圾回收(Java8中已经删除了永久代,使用了元空间Meta Space)

总结,常规的Java堆至少包括了新生代和老年代两块内存区域:

  • 新生代:存活对象少、垃圾多
  • 老年代:存活对象多、垃圾少

新生代:复制回收机制

对于新生代区域,因为每次GC都会有大量新对象死去,存活的较少。所以采用复制回收机制,GC时只把少数存活的对象复制过去便可。

复制算法设计:

  1. 内存1:1 每次只使用一半的内存,当这一半满了后,就进行垃圾回收,把存活的对象直接复制到另外一半内存,并清空当前这一半的内存。

缺陷:至关于只有一半的内存可用,对于新生代而言,新对象会频繁地进行建立,若是只有一半的可用内存,会持续 不断地进行垃圾回收工做,影响了程序的正常运行。

  1. 内存9:1

最开始使用9的内存,当9快满时执行复制回收机制,把9中存活的对象复制到1区,并清空9区。

缺陷:因为内存空间比例相差较大,当把9区存活的对象复制到1区时,颇有可能1区放不下,此时不得不把对象移到老年区。这就意味着,可能会有一部分并不老的9区对象因为1区放不下而被放到老年区,破坏了老年区的规则。

  1. 内存8:1:1(Eden:SurviorA:SurvivorB)

工做原理:

  • Eden区最大,对外提供堆内存。当Eden区快满时,进行Minor GC,把存活对象放入Survivor A区,清空Eden区;
  • Eden区被清空后,继续对外提供堆内存
  • 当Eden区被填满时,对Eden和Survivor A同时进行Minor GC,把存活的对象复制到Survivor B,同时清空Eden、Survivor A
  • Eden继续对外提供堆内存,并重复上一步工做
  • 当某个Survivor被填满,且仍有对象未被复制完毕时,或者某些对象在反复Survive 15次左右时,则把这部分剩余对象放到Old区(老年代)
  • 当Old区也被填满时,进行Major GC,对Old区进行垃圾回收

老年代:标记整理回收机制

老年代通常存放存活时间较久的对象,因此每次GC时,存活对象多,每次只要少数对象被回收。所以,根据不一样回收机制的特色,这里选择标记整理回收机制,仅仅经过少许地移动对象就能清理垃圾,并且不存在内存碎片。

相关文章
相关标签/搜索