前言
垃圾回收一般需要暂停所有线程的执行,叫stop-the-world。GC优化基本就是减少暂停次数和暂停时间。
一、回收哪里的垃圾
JVM的内存大致分为5个区,程序计数器,虚拟机栈,本地方法栈,堆,方法区。
程序计数器
顾名思义跟PC寄存器作用类似,每个线程独立存在,生命周期与线程一致。指示当前执行的方法,内存很小,忽略不计,没有垃圾。
虚拟机栈
栈空间,每个线程独立存在,保存方法参数或者方法内对象的引用。生命周期结束,比如方法执行完毕后内存会被释放,所以不需要垃圾管理。
本地方法栈
与虚拟机栈类似,对应native方法。不需要垃圾管理。
堆
对象的实际存储区域,比如在方法内new一个局部变量,在堆开辟内存,引用保存在虚拟机栈。也是垃圾管理的最主要的区域。
方法区
class文件和常量(JDK7开始字符串常量池在堆区)存储区域,属于垃圾管理范围。
二、确定哪些是垃圾
主要有两个算法,引用计数法,可达性分析法(根搜索算法)。基本所有垃圾回收器都采用可达性分析法。
引用计数法
记录每个引用的被引用个数,当引用个数为0时代表成为垃圾,应该被清理。
优点:
- 实现简单
- 引用减少至0时,实时回收内存,内存利用率高
- 回收操作可并发运行,无需暂停应用线程
缺点:
4. 无法解决环状引用,如a引用b,b引用a。两个都是垃圾却不会被清理(过于致命)
5. Space overhead: 每个对象需要格外空间存储引用次数
6. Speed overhead: 每次引用修改需要增加指令,且多线程情况下需要加锁保证原子操作
可达性分析法(根搜索算法)
确定根对象(GC ROOT),顺着根对象遍历,凡是被根对象直接或者间接引用的都不是垃圾,剩下就是垃圾。 Java中的GC ROOT对象有: 7. 虚拟机栈中引用的对象(本地变量表) 8. 方法区中静态属性引用的对象 9. 方法区中常量引用的对象 10. 本地方法栈中引用的对象(Native对象)
算法优点: 11. 不存在循环引用问题 12. 不存在引用计数法的空间和运行时开销
算法缺点: 13. 内存利用率低(在GC回收内存前,垃圾对象占用的内存无法复用 14. 设计复杂,若需要支持并发回收需要额外数据结构支撑 15. PauseTime: 根集枚举与引用Tracing过程一般需要暂停应用线程至少一次,大部分回收器要暂停2-3次
三、怎么回收垃圾
分代回收
新生代(Young generation):绝大多数最新被创建的对象都会被分配到这里,由于大部分在创