jvm之垃圾收集一之垃圾回收算法

2022-12-24,,,,

最近又重新在读深入理解java虚拟机一书,吸取第一次读完到现在已经忘记的差不都的教训,这次的学习之旅想通过博客的形式记录下自己的所学所感,以备后续继续学习备忘所用!这次先记录下垃圾收集相关知识点:

垃圾收集一般有三件事情要做,一是哪些内存需要回收,二是什么时候回收,三是怎么去回收?

先来确定第一件事,也就是如何来确定需要回收的内存?主要有以下两种实现:

一、引用计数法

具体实现:每个对象内部维护一个引用计数器,对象每被引用一次则计数器加一,反之则减一,当引用计数器的值为0的时候则表示该对象不可能再被使用了;

优点:效率高;尽管维护计数器有一定的内存开销,但原理简单

缺点:不做特殊处理的话,循环依赖的对象无法被回收;这也好理解,比方说创建A,B两个对象,然后将A对象里的属性赋值给B,B在赋值给A(此时A,B两个对象的引用计数器的值至少为1),再将A,B对象均置空,按道理对象都置空了应该是属于可被回收的了,但是由于前面的引用还在计数器的值还是大于0的,按照引用计数法的逻辑是不应该被回收的

二、可达性分析

具体实现:维护一堆叫GC Roots的起始节点集合,从这些节点开始根据引用关系向下搜索,搜索过程所走过的路径称为引用链,如果某个对象到GC Roots没有任何引用链连接,则证明其不可能再被使用

优点:可以回收那些实际已不可能使用,但存在相互依赖的对象

缺点:不做优化GC Roots有可能过度庞大,导致判断效率降低

那些固定可作为GC Roots的对象:

虚拟机栈(栈帧中的本地变量表)中引用的对象,如各线程被调用的方法堆栈中使用到的参数,局部变量,临时变量等

方法区中类静态属性引用的对象,如java类的引用类型静态变量

方法区中常量引用的对象,如字符串常量池中的引用

本地方法栈中JNI(Native方法)引用的对象

虚拟机内部的引用,如基本数据类型对应的class对象,常驻的异常对象,系统类加载器

所有被同步锁(synchronized修饰)持有的对象

反应java虚拟机内部情况的JMXBean,JVMTI中注册的回调,本地代码换成等

然后确定第二件事情,什么时候进行回收?

需要注意的事,即使当某一对象到GC Roots已不可达,但是在GC之前还是会调用一次对象的finalize方法,如果finalize方法没有成功自救,则对象基本就会被回收了;

最后确定第三季事,怎么去回收?

常用的垃圾回收算法有以下几种:

一、标记-清除

将所有已被认定需要回收的对象打上标记,然后统一清除,或者反过来,标记那些存活的对象,清除没打标记的对象内存;这种做法简单粗暴且高效,但是由于回收的对象分布在堆中的各处,所以回收后会造成内存空间的不连续,这样不利于后期分配需要占大内存的对象

二、标记-复制(常用于新生代)

基于标记清除算法,针对其可能造成的内存空间不完整进行优化,具体做法是将内存分成两等块,一块正常分配对象,另一块用于垃圾收集后存放存活的对象,当一块对象分配满了需要GC时将所有存活的对象复制到一边,只回收另一边的内存,从而解决了回收后内存不规整的问题,但是考虑到复制成本,效率肯定会低于标记清除,并且将内存分成两块,明显浪费了内存空间,需要额外空间来做担保(老年代),以防止GC时对象全部存活的极端情况

三、标记-整理(常用语老年代)

同样基于标记清楚算法,前期的标记过程也一致,但是为了解决标记清除造成的空间不连贯以及标记复制可能造成的效率低下问题,标记整理算法在打完标记后,不是直接对对象进行清理,而是让所有存活的对象像某一端移动,然后直接清理掉边界以外的内存;通常在老年代使用

四、分代收集算法

根据对象存活周期的不同将对象分成几块,针对每块进行回收,基于以下三种假说:

分代假说

1,弱分代假说

大部分对象都是朝生夕灭的

2,强分代假说

熬过越多次垃圾收集的对象越难被回收

3,跨代引用假说

跨代引用的对象相比同代引用属于极少数

jvm之垃圾收集一之垃圾回收算法的相关教程结束。

《jvm之垃圾收集一之垃圾回收算法.doc》

下载本文的Word格式文档,以方便收藏与打印。