聊聊JVM垃圾回收算法
一个对象被垃圾回收之前,要判断它是否是垃圾对象(不再使用的对象)。
JVM垃圾回收算法
判断对象存活的两种方式:
引用计数算法
原理:为每个对象保存一个整型的引用计数器属性,记录对象被引用的情况,如果被引用着,那么它即为存活对象。
优点:实现简单,便于识别,回收没有延迟性。
缺点:额外内存开销;更新技术器增加时间开销;无法处理循环引用问题。
什么是循环引用
可达性分析算法(java中使用)
原理:对象根集合(GC Roots)为起点,自上向下搜索被其连接的对象。这些对象就是存活对象,其他未直接或间接被连接的就是垃圾对象。
GC Roots有哪些?
1.虚拟机栈中引用的对象
2.本地方法栈内本地方法引用的对象
3.方法区类静态属性引用的对象
4.方法区中常量引用的对象
5.被同步锁持有的对象
6.基本类型对应的Class对象、常驻异常对象等。
如何快速判断一个对象是否是GC Root?
如果一个指针,它保存了堆内存里的对象,但自己又没有存放在堆内存,那他就是一个GC Root。
标记-清楚算法、标记-整理算法都是基于可达性分析算法实现。
三种垃圾收集算法
标记-清除算法
执行过程:
- 标记:垃圾回收器从引用根节点开始遍历,标记所有被引用的对象。一般是在对象的Header中记录为可达对象。
- 清除:垃圾回收器对堆内存从头到尾进行线性遍历,如果发现某个对象的对象头Header中没有标记为可达对象,则将其回收。
总结:
1.效率不高,因为要进行两次遍历。
2.垃圾回收会停止整个应用程序,导致用户体验差。
3.由于直接清理,内存会产生碎片,需要维护一个空闲列表(空闲的内存空间的地址)。
注意: 清除并非真的清除,而是保存了这个位置的地址到空闲列表后,下次有新对象,如果位置足够,直接放这。
标记-整理算法
执行过程:
1.标记,同标记-清除
2.清除,同标记-清除
3.整理内存,不用维护空闲列表
图解:
总结:
1.整理后内存连续,不用维护空闲列表,存对象时只需知道从哪开始存放。
2.相较于复制算法,消除了高额的内存代价。
3.效率在三个算法中最低。移动对象时,如果对象被其他对象引用,还需调整引用的地址。
4.移动过程全程需要暂停用户应用程序(STW)。
复制算法
原理:
将内存空间分为两块,每次只使用一块,将正在使用的存活对象复制到未被只用的内存块中,清除剩余的所有对象,下次回收,交换两块内存的角色。
图解:
总结:
1.没有标记和清除过程,实现简单,运行高效。
2.不会出现内存碎片问题。
3.缺点明细,浪费一半内存空间。
4.如每次存活对象很多,那么复制算法不太理想。因为复制对象也需要时间开销。
三个算法总结
- 三者各有各的优缺点。JVM也是根据这三种算法的特点,安排它们在不同内存场景去回收垃圾对象。
- 分代收集算法思想就是利用这三个算法各自的特点,把它们分别应用到新生代和老年代去。