JVM 垃圾回收原理
JVM 堆是垃圾回收最为频繁的区域。
堆内存划分
新生代
Eden
Survivor spaces
From
To
老年代
永久代
内存分配原则
对象优先在 Eden 区分配,内存不足时,发起一次 Minor GC
大对象直接进入老年代
对象移动到 Survivor 区之后,每经过一次 Minor GC,对象的年龄加 1,年龄达到 15 岁时,对象进入老年代
当 Survivor 区相同年龄的对象大小总和大于 Survivor 的一半,所有大于等于该年龄的对象进入老年代
当 Minor GC 时 Eden 区有大量对象存活并且 Survivor 区不能容纳时,老年代进行担保,将不能容纳的对象分配到老年代
对象引用类型
强引用:这种是用的最多的,jvm 内存不足会抛出内存溢出异常也不会被 gc 收集
软引用:用来表示系统中有用但是非必须的对象,当 jvm 内存不足时,会立即回收这些内存。
弱引用:无论内存是否足够,一旦发生 gc,立即进行内存回收。
虚引用:我们无法通过虚引用获得一个类的实例,因为 jvm 可能在任何时刻回收这个对象,唯一的作用就是用于追踪这个对象被回收掉的通知
对象是否可回收
JVM 有两种方式判断对象是否可以被回收,第一种是引用计数法,第二种是可达性分析算法。
引用计数法
是对对象添加一个引用计数器,每当被引用时,计数器加 1,引用失效时,计数器减 1,当计数器为 0 时,表示对象可以被回收了,但是这种方式解决不了相互循环引用的问题
可达性分析算法
是定义了 GC Roots 这样一类对象作为起始点,当一个对象到 GC Roots 没有任何引用链时,表示对象可以被回收了。GC Roots 对象包括:
栈帧的本地变量表中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
本地方法栈中引用的对象
垃圾回收算法
标记清除(新生代)
在内存中标记要被回收的对象,标记完成后,统一回收被标记的对象。
缺点:
标记和清除过程效率低
标记清除后,内存出现很多碎片,如果空间碎片不足以给新的对象分配内存,那么会触发一次新的垃圾回收
复制算法(新生代)
将内存分为大小相等的两块,每次只用其中的一块,当这一块内存用完了,就把还存活的对象复制到另一块内存上,然后把刚才那一块内存空间一次清理掉(Form 和 To)
优点:不再有空间碎片
缺点:
内存一分为二
对象存活数量多时,可能要进行多次复制,进行垃圾回收,效率低,并且内存不够时,可能会直接使用老年代进行空间担保,分配内存
标记整理清除(老年代使用)
在内存中标记要被回收的对象,仍然存活的对象都向内存的一端移动,然后清理掉回收的区域
分代收集算法
将内存分配成多个区域,例如新生代、老年代等,根据不同区域使用不同的回收算法
评论