架构师训练营第九周作业

/ 架构师训练营 / 没有评论 / 736浏览

请简述 JVM 垃圾回收原理。

1. Java中的四种引用类型

在Java中,对于引用最基本的解释就是:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用(有点指针的意味)。后来Java还将引用划分为了4种,根据被GC回收的时机可以分为:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantorm Reference)。这4种引用的强度依次渐弱。

2.如何判断对象需要被回收

2.1 引用计数法

在对象内部会有一个引用计数器,一旦某个地方引用它时,计数器就加1,表面上看这是一个效率非常高的方式,但是如果出现循环引用时,采用了这种方式判断对象是否存活的GC就难以发挥作用了。

2.2 可达性分析法

相比之下,可达性分析算法就要靠谱得多。所谓可达性分析就是通过一系列被称为“GC Roots”的点作为起始点,从这些节点开始向下搜索,搜索的路径称为引用链,当一个对象到GC Roots不可达的时候,则证明此对象是可回收的。

3.垃圾回收算法

3.1 标记清除法

标记清除法有两个过程,

这种回收算法非常简单粗暴,也很好理解,但是暴露出来问题还是比较大的,需要优化的地方还有很多。

3.2 复制算法

为了解决内存碎片问题,复制算法出现了,它将可用内存平均划分为两块,每次只使用其中一块。当这一块用完了,就将还存活的对象复制到另一块,然后再把已使用的内存空间一次清理掉。这样每次GC的时候都直接回收半个内存空间的大小,不必考虑碎片问题。但这种方法带来的代价也不小,牺牲了一半的存储空间。

3.3 标记整理法

如果在对象存活率较高的时候采用复制算法的话,复制的操作就会有很多,对程序的运行效率也会产生一定的影响,此时就应该考虑标记整理法。标记整理法的执行过成分为3步:

  1. 用与“标记清除法”一样的操作标记存活的对象;
  2. 将被标记的对象全部移动到内存的某一端;
  3. 清除边界以外部分的内存。

3.4 分代回收法

在分代回收算法中,会根据对象的存活周期,将内存划分为几块,一般是新生代(Young Generation)、老年代(Old Generation)、永久代(Permanent Generation)。这样就可以根据不同内存区域的特点执行采用不同的回收算法。像新生代这种经常有大批对象死去的区域,就适合用复制算法。而对于老年代这种对象生存周期较长和永久代这种内存存活率较高,又没有其他担保空间的地方就用标记清除法或标记整理法就行了。

4.内存分配策略

JVM的一大优势是解决的内存方面的两个重要的问题:自动给对象分配内存 和 自动回收分配给对象的问题。一般来说,分配对象需要符合以下原则:

4.1 对象优先在Eden分配

当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC。

4.2 大对象直接进入老年代

所谓的大对象是指:需要大量连续内存空间的Java对象。最典型的大对象就是那种很长的字符串以及数组。这样可以有效避免大对象在Eden和Survivor之间的频繁复制操作带来的性能开销。(当然我们在写代码的时候也要注意避免写出生命周期太短的大对象,因为这并不符合老年代的特点)

4.3 长期存活的对象将会进入老年代

根据老年代的特点,每当对象熬过了一次Minor GC的时候,它的age就将会增加1,当年龄增长到一定的值时(默认为15岁),它就将进入老年代。

4.4 动态对象年龄判定

对于“老年对象”的判定并不一定是要根据”默认的年龄要求(15岁)“,如果在Survivor空间中相同年龄所有对象的大小的总和大于Survivor空间的一半的时候,年龄大于或等于该年龄的对象将会直接进入老年代。