目录
垃圾回收概念
什么是垃圾?
垃圾回收机制什么时候会进行GC??
应该关心垃圾回收那些哪些区域的回收
垃圾回收相关算法
垃圾回收算法:[标记阶段、回收阶段]
垃圾标记阶段
标记阶段的目的
引用计数算法(目前没有在使用)
什么是循环依赖问题??
可达性分析算法(当前使用的算法)
什么是根对象??
垃圾回收阶段
标记-复制算法(Copying)
标记-清除算法(Mark-Sweep)
标记-压缩算法(Mark-Compact)
对象的 finalization 机制
finalize()方法
对象在虚拟机中的三种状态
垃圾回收相关概念
内存溢出与内存泄漏
STW (stop the world)
垃圾回收器
垃圾回收算法是理论,垃圾回收器是回收的实践者
垃圾收集器分类
按线程数分类
单线程垃圾回收器(Serial)
多线程垃圾回收器(Parallel)
按工作模式分
独占式垃圾回收器(STW)
并发式垃圾回收器
按工作的内存区间分
年轻代的垃圾回收器
老年代的垃圾回收器
GC性能指标
CMS回收器(并发标记清除)[追求低停顿]
垃圾回收过程
初始标记:
并发标记:
重新标记:
并发清除:
G1垃圾回收器
垃圾回收过程
初始标记:
并发标记:
最终标记:
筛选回收:
垃圾回收概念
什么是垃圾?
垃圾是指在运行程序中没有任何引用指向的对象,这个对象就是垃圾,垃圾对象需要被清理回收,否则会一直被占用,浪费空间,其他新对象无法使用该控件,严重的话会造成内存溢出
垃圾回收机制什么时候会进行GC??
- 堆内存不足:当JVM的堆内存不足以分配新的对象时,会触发垃圾回收以释放不再使用的内存空间。
- 系统调用:可以通过调用
System.gc()
或Runtime.getRuntime().gc()
来建议JVM进行垃圾回收,但具体何时执行是由JVM决定的。- 作用域结束:当对象的作用域结束时,例如方法执行完毕,该作用域内的局部变量将不再被引用,这些对象就可能被回收。
- 程序正常退出:当程序执行了
System.exit()
方法时,JVM在退出前会进行垃圾回收以清理资源。- 对象引用丢失:如果一个对象没有任何引用指向它,那么这个对象就成为垃圾回收的候选对象。
- 内存泄漏检测:某些JVM实现可能会定期检查内存使用情况,以识别和处理内存泄漏问题。
- 定时回收:一些JVM实现可能会有一个定时机制,定期执行垃圾回收以保持内存的整洁。
- 老年代空间不足:当老年代空间不足以容纳新晋升的对象或不足以进行Minor GC时,会触发Full GC。
- 元数据区溢出:在Java 8及以后的版本中,如果元空间(Metaspace)不足以存储类的元数据,也可能触发垃圾回收。
- 新生代空间不足:当新生代空间不足以分配新对象时,会触发Minor GC。
- Survivor空间溢出:如果Survivor空间中的对象太多,无法容纳新的对象,也会触发Minor GC。
应该关心垃圾回收那些哪些区域的回收
重点回收堆{
频繁回收新生代
较少回收老年代
}
较少回收方法区
垃圾回收相关算法
垃圾回收算法:[标记阶段、回收阶段]
垃圾标记阶段
标记阶段的目的
标记阶段的目的主要是为了判断对象是否为垃圾对象
在 GC 执行垃圾回收之前,首先需要区分出内存中哪些是有用对象,哪些是垃圾对象。只有被标记为己经是垃圾对象,GC 才会在执行垃圾回收时,释放掉其所占用的内存空间,因此这个过程我们可以称为垃圾标记阶段。
那么怎样判定一个对象是否为垃圾对象呢?简单来说:当一个对象没有任何引用指向时,就可以称之为垃圾对象。
引用计数算法(目前没有在使用)
为每个对象保存一个整型的引用计数器属性,用于记录对象被引用的情况
对于一个对象A,只要有一个引用指向了对象A,则对象A的引用计数器属性加1;当引用失效时,引用计数器就减1;只要对象A不可能再被使用,则表示可进行回收。
优点:实现简单,垃圾对象便于辨识;判定效率高,回收没有延迟性
缺点:
这样的做法增加了存储空间的开销(占内存)
每次操作都需要更新计数器,增加了时间开销(费时间)
不可处理循环依赖问题
什么是循环依赖问题??
当外界有引用指向这个系统时,A的引用会指向B,B的引用指向C,C引用又指向A,这样如果外部请求引用切断时,该系统与外界就失去了联系,但是他们的程序计数器属性不为0,这样会导致循环引用问题,从而导致内存泄漏
可达性分析算法(当前使用的算法)
可达性分析:也可称之为根搜索算法、追踪性垃圾收集
相较于引用计数算法,可达性分析算法有效的解决了引用计数算法中的循环引用问题,防止内存泄露问题的发生。
从一些活跃对象(GC Roots)开始搜索,与跟对象相关联的对象都是被使用的对象,与根对象或者根对象的引用链不相连的,则称之为垃圾对象。
什么是根对象??
- 虚拟机栈中引用的对象(正在运行的方法中的对象)
- 静态属性(static)
- 被用来当做同步锁的
- Java系统中的类
垃圾回收阶段
标记-复制算法(Copying)
可以有多块内存,每次至少有一块是空闲的,复制算法会将存活的对象移动到未被使用的空间中,清除其它块中所有的垃圾对象。
特点:内存碎片少,适用于存活对象少,垃圾对象多的区域(适用于新生代)
标记-清除算法(Mark-Sweep)
存活对象的位置不变,垃圾对象的地址记录在一个空闲的列表中,如果创建新对象,则将空闲列表中垃圾对象覆盖掉。
特点:不移动对象,回收后会产生内存碎片(适用于老年代)
标记-压缩算法(Mark-Compact)
标记-清除算法+重新排列
将存活的对象重新排列,其余空间进行清理,又称:[标记-清除-压缩算法]
特点:回收后压缩,不会产生内存碎片(适用于老年代)but效率低
对象的 finalization 机制
Java 语言提供了对象终止(finalization)机制来允许开发人员提供对象被销毁 之前的自定义处理逻辑(如Servlet生命周期中的destroy()方法)
finalize()方法
Object类中,在对象回收之前,可以在此方法中执行一些所需要的逻辑(close()操作等),在对象被判定为垃圾对象时,回收之前会调用finalize()方法,finalize()方法只会被调用一次。
对象在虚拟机中的三种状态
- 可触及的:从根节点可到达的。
- 可复活的:被标记为垃圾对象,但还未finalize()的。
- 不可触及的:finalize()已调用,且被垃圾回收机制执行的。
垃圾回收相关概念
内存溢出与内存泄漏
内存溢出:
内存溢出Out Of Memory,简称OOM
经过垃圾回收后,内存仍然不够,导致程序崩溃。
大多数情况下GC会根据各个年龄段的不同来进行垃圾回收,但是 如果实在回收不了,就会进行 Full GC ,这时候会释放出大量的空间,以供程序应用继续使用。
存溢出可能导致程序崩溃或系统的不稳定。
内存泄漏:(内存浪费)
一个对象在程序中不会被使用,但是垃圾回收器不能回收,会一直占用内存,进而导致内存泄漏(e.g:IO,数据库连接等为close()、单例模式中单例对象,整个程序中使用唯一的对象,如果不关闭,回收器就无法进行回收)
STW (stop the world)
Stop-the-World,简称 STW,当垃圾回收时(标记/回收)会导致其他用户线程暂停,必须保证分析时,其他对象的引用不会出现变化,避免出现漏标、错标等问题,保证分析的准确性。
垃圾回收器
垃圾回收算法是理论,垃圾回收器是回收的实践者
垃圾收集器分类
按线程数分类
单线程垃圾回收器(Serial)
适用于一些小的设备,只有一个线程进行垃圾回收
多线程垃圾回收器(Parallel)
提供多个线程进行垃圾回收
按工作模式分
独占式垃圾回收器(STW)
垃圾回收线程执行时,其他用户线程暂停
并发式垃圾回收器
垃圾回收线程可以和用户线程同时执行
按工作的内存区间分
年轻代的垃圾回收器
老年代的垃圾回收器
GC性能指标
吞吐量:运行用户代码的时间占总运行时间的比例(总运行时间:程序的运行时间+内存回收的时间)
垃圾收集开销:垃圾收集所用时间与总运行时间的比例。
暂停时间:执行垃圾收集时,程序的工作线程被暂停的时间。
内存占用:Java 堆区所占的内存大小。
CMS回收器(并发标记清除)[追求低停顿]
首个实现垃圾收集线程与用户线程可以同时执行的。
注意:不是所有都并发执行,也会有独占执行。
垃圾回收过程
初始标记:
Stop The World,仅使用一条初始标记线程对所有与 GC Roots 直接关联的对象进行标记。
并发标记:
垃圾回收线程,与用户线程并发执行。此过程进行可达性分析,标记出所有废弃对象。
重新标记:
Stop The World,使用多条标记线程并发执行,将刚才并发标记过程中新出现的废弃对象标记出来。
并发清除:
只使用一条 GC 线程,与用户线程并发执行,清除刚才标记的对象。 这个过程非常耗时。
并发标记与并发清除过程耗时最长,且可以与用户线程一起工作,因此,总体上说,CMS 收集器的内存回收过程是与用户线程一起并发执行的。
G1垃圾回收器
适合大型服务器,内存大,CPU更先进,将每个区域(Eden,幸存者,老年代)又划分成若干小的区域,哪个区域垃圾数量多,优先回收哪个区域。可以做到整堆管理收集回收,也可以并发收集回收。
垃圾回收过程
初始标记:
标记出 GC Roots 直接关联的对象,这个阶段速度较快,需要停止用户线程,单线程执行。
并发标记:
从 GC Root 开始对堆中的对象进行可达性分析,找出存活对象,这个阶段耗时较长,但可以和用户线程并发执行。
最终标记:
修正在并发标记阶段引用户程序执行而产生变动的标记记录。
筛选回收:
筛选回收阶段会对各个区域的回收价值和成本进行排序,根据用户所期望的 GC 停顿时间来指定回收计划(用最少的时间来回收包含垃圾最多的区域)。
这就是 Garbage First 的由来——第一时间清理垃圾最多的区块,这里为了提高回收效率,并没有采用和用户线程并发执行的方式,而是停顿用户线程。
适用场景:要求尽可能可控 GC 停顿时间;内存占用较大的应用。