写点什么

JVM 垃圾回收原理

用户头像
A p7+
关注
发布于: 2020 年 11 月 22 日

JVM 堆是垃圾回收最为频繁的区域。

堆内存划分

  • 新生代

  • Eden

  • Survivor spaces

  • From

  • To

  • 老年代

  • 永久代


内存分配原则


  1. 对象优先在 Eden 区分配,内存不足时,发起一次 Minor GC

  2. 大对象直接进入老年代

  3. 对象移动到 Survivor 区之后,每经过一次 Minor GC,对象的年龄加 1,年龄达到 15 岁时,对象进入老年代

  4. 当 Survivor 区相同年龄的对象大小总和大于 Survivor 的一半,所有大于等于该年龄的对象进入老年代

  5. 当 Minor GC 时 Eden 区有大量对象存活并且 Survivor 区不能容纳时,老年代进行担保,将不能容纳的对象分配到老年代


对象引用类型


  • 强引用:这种是用的最多的,jvm 内存不足会抛出内存溢出异常也不会被 gc 收集


Object o = new  Object();
复制代码


  • 软引用:用来表示系统中有用但是非必须的对象,当 jvm 内存不足时,会立即回收这些内存。

SoftReference<String> s = new SoftReference<>(new String("Hello"));System.out.println(s.get());// Hello
复制代码

  • 弱引用:无论内存是否足够,一旦发生 gc,立即进行内存回收。

WeakReference<String> w = new WeakReference<>(new String("Hello"));System.out.println(w.get());// HelloSystem.gc();System.out.println(w.get());// null
复制代码

  • 虚引用:我们无法通过虚引用获得一个类的实例,因为 jvm 可能在任何时刻回收这个对象,唯一的作用就是用于追踪这个对象被回收掉的通知

ReferenceQueue<String> queue = new ReferenceQueue<>();PhantomReference<String> pr = new PhantomReference<>(new String("Hello"), queue);System.out.println(pr.get());
复制代码


对象是否可回收

JVM 有两种方式判断对象是否可以被回收,第一种是引用计数法,第二种是可达性分析算法。

  • 引用计数法

是对对象添加一个引用计数器,每当被引用时,计数器加 1,引用失效时,计数器减 1,当计数器为 0 时,表示对象可以被回收了,但是这种方式解决不了相互循环引用的问题


  • 可达性分析算法

是定义了 GC Roots 这样一类对象作为起始点,当一个对象到 GC Roots 没有任何引用链时,表示对象可以被回收了。GC Roots 对象包括:

  1. 栈帧的本地变量表中引用的对象

  2. 方法区中类静态属性引用的对象

  3. 方法区中常量引用的对象

  4. 本地方法栈中引用的对象


垃圾回收算法


  • 标记清除(新生代)

在内存中标记要被回收的对象,标记完成后,统一回收被标记的对象。

缺点:

  1. 标记和清除过程效率低

  2. 标记清除后,内存出现很多碎片,如果空间碎片不足以给新的对象分配内存,那么会触发一次新的垃圾回收

  • 复制算法(新生代)

将内存分为大小相等的两块,每次只用其中的一块,当这一块内存用完了,就把还存活的对象复制到另一块内存上,然后把刚才那一块内存空间一次清理掉(Form 和 To)

优点:不再有空间碎片

缺点:

  1. 内存一分为二

  2. 对象存活数量多时,可能要进行多次复制,进行垃圾回收,效率低,并且内存不够时,可能会直接使用老年代进行空间担保,分配内存

  • 标记整理清除(老年代使用)

在内存中标记要被回收的对象,仍然存活的对象都向内存的一端移动,然后清理掉回收的区域

  • 分代收集算法

将内存分配成多个区域,例如新生代、老年代等,根据不同区域使用不同的回收算法

用户头像

A p7+

关注

还未添加个人签名 2020.06.05 加入

还未添加个人简介

评论

发布
暂无评论
JVM垃圾回收原理