图文讲解java垃圾回收机制

723 阅读7分钟

1. JAVA GC 概述

JAVA GC采用了分代思想,将java堆分成新生代,年老代,永久代。GC算法主要有标记-清除,标记-压缩,复制算法。

  • 新生代:新生代被分成三个部分 eden区和2个survivor区(from和to两个分区)。当创建对象,需要jvm分配内存时,会在新生代的eden区寻找合适的内存区域。如果当eden区内存不够时,会触发minor GC。eden区存活对象和from区的存活对象将会被复制到to区。当to区的对象年龄超过了晋升的年龄设置,对象将被提升到老年代。新生代GC用的是复制算法
  • 年老代: 年老代里存放的都是存活时间较久的,大小较大的对象,因此年老代使用标记整理算法。当年老代容量满的时候,会触发一次Major GC(full GC),回收年老代和年轻代中不再被使用的对象资源。年老代算法用的是标记-清除
  • 永久代:指内存的永久保存区域,主要存放Class和Meta(元数据)的信息。Class在被加载的时候被放入永久区域。它和和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的Class的增多而胀满,最终抛出OOM异常。

2. 图文描述标记-清除和标记-压缩

Step 1:标记(Marking)

GC的第一步叫做标记。在这个步骤GC通过遍历内存区辨别哪些内存在使用,哪些内容没有使用。并做好标记

image

如上图蓝色的表示存活的对象,金黄色表示垃圾对象。在标记阶段,需要扫描整个该内存区的对象,并标记。这个过程可能会比较耗时

Step 2: 清除(Normal Deletion)

清除阶段移除掉垃圾对象,并且用一个链表维护空闲的区域

内存分配器持有空闲内存区的引用,以便分配内存给新的对象

Step 2a: 压缩(Deletion with Compacting)

为了提升性能,在Step 2的基础上,在删除完垃圾对象后。我们可以把存活的对象移动到内存区的头部。这样下次分配内存的时候会更快。主要原因是标记-清除会造成比较大的内存碎片,每当需要分配内存时,都需要遍历空闲链表。而压缩算法,会把内存碎片整理成一个大的完整内存块。

image

3. 分代垃圾回收

3.1 为什么要采用分代垃圾回收?

正如前面所说,标记和压缩对象,对java虚拟机而言会比较耗时。当java虚拟机分配了越来越多的对象后。GC所花费的时间将会更长。然而根据经验分析,绝大多数的对象存活时间都比较短。这样我们可以把存活长的对象和存活短的对象隔离开。这样GC会更加高效

image

3.2 JVM分代

将jvm堆内存分割成更小的内存区,会提高jvm的gc性能。堆被分成 (新生代)Young Generation,(年老代)Old or Tenured Generation, and (永久代)Permanent Generation

image

  • 所有的对象都会在新生代中分配内存。当新生代内存不够的时候。将会出发minor GC。如果新生代中的对象存活时间都很短,那么minor GC的效率将会很高。如果新生代里面充满了垃圾对象,那么回收速度将会很快(因为标记的时间短了)。一些存活下来的对象年龄将会增加,并且最终会被移动到年老代
  • Stop the World Event:所有的minor GC都是"Stop the World"事件。这意味着除了GC线程,程序的其他线程对将暂停知道GC完成。Minor GC都是Stop the World Event
  • 年老代是用来存储长存活时间的对象。典型的我们可以给对象设定一个年龄界限,当新生代的对象存活年龄超过这个界限,对象将会从新生代移动到年老代。当年老代的内存不够的时候,将会出发Major GC。Major GC也是Stop the World Event。通常来说Major GC比Minor GC更慢,因为Major GC回收的是整个新生代和年老代的所有垃圾对象。因此对于响应性高的程序,应该尽量减少Major GC。需要注意的是Stop the World Event的时间受到在年老代中使用的垃圾回收器的影响,不受新生代的影响
  • 永久代包含了JVM的元数据。包括类信息,方法信息等。永久代由JVM在应用运行期生成。另外 javase的类库中的类信息也可能存放在这里

3.3 分代垃圾回收的处理过程

现在你明白为什么堆分成不同的世代,现在是时候看看这些空间是如何相互作用的。 下面的图片将介绍JVM中的对象分配和老化过程。

1. 一开始,任何新的对象都会在eden空间分配内存,两个survivor空间一开始都是空的

image

2. 当eden空间被填满了,minor GC将被触发

image S0 survivor区对象里的 1 3表示对象的年龄

3. Eden空间的存活的对象将被复制到第一个survivor空间,年龄+1,垃圾对象将会被清除掉

4. 下一次minor GC发生时,Eden空间的存活对象将被复制到空闲的survivor空间S1(年龄+1),另外在前一次minor GC S0空间的存活对象也会被复制到S1(年龄+1),垃圾对象会被清除掉

image

5. 下一次minor GC发生时,还是重复第4条的内容,只是两个survivor空间对调了,这次是从S1复制到S0空间

6. 新生代晋升到年老代。当minor GC发生时,如果survivor空间中的对象年龄超过了晋升的年龄限定,对象会被复制到年老代 image

7. 当minor GC不断触发,将会有对象不断被晋升到年老代 image

8. 上面的图文完美的解释了minor GC的处理过程。最终,当年老代的内存被填满的时候,将会触发major GC。Major GC在年老代用的是标记压缩算法。同时新生代的对象将被清除 image

推荐阅读

事件分发四部曲


1.1. 事件分发四部曲之一深度遍历讲解事件分发

1.2. 事件分发四部曲之二详细讲解Android嵌套滑动机制

1.3. 事件分发四部曲之三详细讲解CoordinatorLayout事件分发机制

1.4. 事件分发四部曲之四详细讲解BottomSheetBehavior事件分发机制

RecyclerView相关


2.1. RecyclerView滚动时回收和复用机制

2.2. 记录瀑布流布局遇到的一个坑

2.3. 详细讲解RecyclerView动画实现原理之一

2.4. 详细讲解RecyclerView动画实现原理之二

2.5. 聊聊RecyclerView缓存机制

Material Design相关


3.1. 使用AppBarLayout实现京东分类页面二次吸顶功能

3.2. 了解AppBarLayout应该从这几个方面入手

3.3. 一文搞懂BottomSheetBehavior

开源库相关


4.1. 我开源了一个RecyclerView吸顶库

4.2. ViewCompat.offsetTopAndBottom翻车现场

欢迎关注我的公众号:字节小站


这是一个分享Android技术干货的公众号,如果你觉得我的文章能够让你学习到东西,希望你可以帮我分享其他更多的人。如果你有好的文章,欢迎投稿,让我们一起来分享。