垃圾
没有任何引用指向的对象都是垃圾
找垃圾的方法
- 引用计数法:一个对象创建时会分配一个计数器,有引用指向这个对象时,计数器+1,当引用失效,计数器-1,当计数器为0时,就可以判断为一个垃圾对象(这种方法解决不了循环引用的问题)
- 根可达算法:以GC Roots为根展开引用链路搜索,如果一个对象没有任何链路跟GC Roots关联,那么就可以判断为一个垃圾对象
- GC Roots的对象:线程栈变量,静态变量,常量池,JNI指针(本地方法对象)
垃圾清除的算法
- 标记清除算法:
- 优点:标记处垃圾对象后清除,算法相对简单,存活对象多的情况效率较高
- 缺点:两边扫描(找出有用对象,找出垃圾对象并清理)效率偏低,容易产生碎片。
- 拷贝算法:
- 优点:适用于对象较少的情况,只扫描一次,效率提高没有碎片
- 缺点:空间浪费,移动复制对象需要调整对象引用
- 标记压缩算法:
- 优点:不会产生碎片,方便对象分配,不会内存减半
- 缺点:扫描两次,需要移动对象,效率偏低。
堆区内存分步图
进入老年代年龄的参数
-XX:MaxTenuringThreshold
栈上分配
- 线程私有小对象
- 无逃逸(没有被外层引用)
- 支持标量替换(使用普通类型代替对象)
- 无需调整(不需要GC优化)
线程本地分配 TLAB(Thread Local Allocation Buffer)
- 占用eden,默认1%
- 多线程的时候,不用竞争,eden就可以申请空间,提高效率
- 小对象
- 无需调整
对象何时进入老年代
超过tenuringThreShould 指定次数(YGC)
- Parallel Scavenge:15
- CMS:6
- G1:15
动态年龄
Eden和S1在GC中存活的对象,超过了S2容量的50%,年龄大的直接放入老年代
分配担保
YGC期间,有对象进来,survivor区的空间不够了,空间担保直接进入老年代
对象创建内存分步流程图
常见的垃圾回收器
Jdk诞生Serial追随,提高效率诞生了PS,为了配合CMS产生了ParNew,1.4后期诞生了CMS,CMS是里程碑式的GC,它开启了并发回收的过程,但是CMS毛病较多,因此目前任何一个JDK版本默认是CMS 并发垃圾回收是因为无法忍受STW
- Serial:年轻代串行回收
- Serial Old:单线程在老年代
- PS:年轻代,并行回收
- ParNew:年轻代配合CMS的并行回收
- CMS:
- 初始标志:标记根对象
- 并发标记:占失败80%,一遍产生垃圾,一边标记
- 重新标记:STW
- 并发清理:会产生浮动垃圾
缺点:内存碎片化,浮动垃圾 解决方案:降低触发CMS阈值-XX:CMSinitiation Occupancy Fraction
92%减少到65%甚至更低,保证老年代有足够的空间。