垃圾回收
垃圾回收(Garbage Collection 简称GC),主要任务就是回收对象释放内存空间,当遇到内存泄漏,内存溢出,或者当并发量达到一个瓶颈时就需要内存回收技术。
分代回收
Java的堆内存采取的是分代管理。根据不同的对象所处的不同时期,回收相应的对象,
新生代主要存放刚创建的对象,其中大部分对象在Eden区,当Eden满时,还存活的会分别存放到From Survivor ,再到To Survivor交替保存,达到一定的次数时会进入到老年代(从新生代存活较长时间的对象过来的),而永久代一般都是对象类信息等内容。
永久代主要有以下信息:
- 类元数据:包括类的名称、父类、实现的接口、成员变量和方法等信息。这些信息在类加载时被加载到永久代中。
- 字符串常量:字符串常量在 Java 中被存储在永久代中,以便于在运行时快速访问。
- JNI 本地引用:JNI 是 Java Native Interface 的缩写,它允许 Java 代码与本地代码进行交互。JNI 本地引用在永久代中存储,以便于与本地代码进行交互时能够快速查找和使用。
判断对象已死
引用计数法
给对象添加一个引用计数器,如果有其他的对象引用了则计数器加1,如果引用失效,则计数减一,当计数器为0是则代表对象无任何引用,则代表这个对象不会被使用,可以被回收。
缺点:无法判断循环引用。
可达性分析算法
任何对象都有一个GC Root是对象作为起始点,从这个起始节点向下搜索,搜索走过的路径称为引用链,当一个对象到GC Root没有任何引用链(无法达GC Root对象则代表该对象不可达),则证明该对象没有被引用,没有被使用,也就是可以被回收。
垃圾收集算法
标记-清除算法
标记-清除算法分为两个阶段“标记”和“清除”,首先标记除所有需要回收的对象,在标记完成之后统一回收所有标记的对象。
标记-清除算法缺点:最大的问题就是效率问题,因为标记和清除的两个过程效率偶不是很高,并且存在另一个问题就是空间问题,标记清除后会产生不连续的空间,如果我们要创建一个数组需要一段连续的空间时,这时会导致空间上的不足。
复制算法
将可用内存按容量划分为大小相等的两块,每次只使用其中一块,当这块内存用完后,就将还存活着的对象复制到另一上面,然后再把已使用的内存空间一次性清理掉。
复制算法缺点:需要首先内存缩小一般,这无疑是对内存的一种浪费,并且会大量的复制操作效率也不是很高。
标记-整理算法
标记整理算法:第一步还是标记需要回收的对象,第二步就是将所有存活的对象都想一边移动,然后直接清理掉边界外的内存。
分代收集算法
在新生代中,每次垃圾收集会有大量对象死去,只有少量对象存活,那就选择复制算法,只需要少量的复制成本就可以完成收集,二老年代中因为对象存活率高,没有额外的空间可以使用“标记-清理”或者“标记-整理算法”
CMS算法
CMS是JDK1.7以前jvm主流的算法,使用标记清除算法,优点并发收集,停顿小。
Stop The World(STW)这里面的"停止",停止所有线程进行回收。
处理过程:
第一个阶段初始阶段标记从GC Roots可达对象
第二个阶段并发标记标记GC线程和应用线程并发执行标记可达对象
第三个阶段对对象重新标记并扫描
第四个阶段并发清理阶段并发垃圾清理
第五个阶段对下一个为下一次GC重新数据结构
G1算法
G1算法在JDK1.9之后成为了默认的GC算法
新生代回收:
并行复制(STW),新生代使用复制算法。
老年代回收:
初始标记(STW)
并发标记
最终标记(STW)
复制/清除(STW)
ZGC算法
jdk11提出,在jdk17实际确认为jvm默认GC算法,针对大内存堆的低延迟垃圾回收算法(后续整理实在太多了)
着色指针
读屏障
并发处理
基于Region
内存压缩(整理)