1.垃圾回收机制
(1)什么是垃圾回收机制(Garbage Collection, 简称GC)
- 指自动管理动态分配的内存空间的机制,自动回收不再使用的内存,以避免内存泄漏和内存溢出的问题
- 最早是在1960年代提出的,程序员需要手动管理内存的分配和释放
- 这往往会导致内存泄漏和内存溢出等问题,同时也增加了程序员的工作量,特别是C++/C语言开发的时候
- Java语言是最早实现垃圾回收机制的语言之一,其他编程语言,如C#、Python和Ruby等,也都提供了垃圾回收机制
(2)JVM自动垃圾回收机制
-
指Java虚拟机在运行Java程序时,自动回收不再使用的对象所占用的内存空间的过程
-
Java程序中的对象,一旦不再被引用会被标记为垃圾对象,JVM会在适当的时候自动回收这些垃圾对象所占用的内存空间
-
优点
- 减少了程序员的工作量,不需要手动管理内存
- 动态地管理内存,根据应用程序的需要进行分配和回收,提高了内存利用率
- 避免内存泄漏和野指针等问题,增加程序的稳定性和可靠
-
缺点
- 垃圾回收会占用一定的系统资源,可能会影响程序的性能
- 垃圾回收过程中会停止程序的执行,可能会导致程序出现卡顿等问题
- 不一定能够完全解决内存泄漏等问题,需要在编写代码时注意内存管理和编码规范
2.垃圾回收算法
(1)引用计数法
- 基本思想,跟踪每个对象被引用的次数,当引用次数为0时,就可以将该对象回收
- 在JVM中,每个对象都有一个引用计数器,当对象被引用时,引用计数器+1
- 当对象被取消引用时,引用计数器-1
- 当引用计数器为0时,该对象就可以被回收
- 优点
- 实现简单,回收垃圾的效率高
- 缺点
- 循环引用无法回收。如果两个对象互相引用,它们的引用计数器永远不会为0,因此无法被回收
- 引用计数器开销大,每个对象都需要一个引用计数器,如果对象很多,开销就会很大
public class Main {
public static void main(String[] args) {
A a = new A();
B b = new B();
a.setB(b);
b.setA(a);
a = null;
b = null;
System.gc();
}
}
class A {
private B b;
public void setB(B b) {
this.b = b;
}
}
class B {
private A a;
public void setA(A a) {
this.a = a;
}
}
- 类A和类B相互引用,每个对象都持有对方的引用,形成了一个循环引用的环,当Main方法执行完毕后,a和b对象都置为null
- 由于它们相互引用,它们的引用计数器都不为0,无法被垃圾回收器回收,导致内存泄漏
- 但是上面代码却不会发生内存泄漏,因为多数jvm没有采用这个引用计数器方案,而是采用可达性分析算法
(2)可达性分析算法
- 可达性分析算法的基本思想是通过一系列的“GC Roots”对象作为起点进行搜索。
- 如果“GC Roots”和一个对象之间没有可达路径,则称该对象是不可达的,不过要注意的是被判定为不可达的对象不一定就会成为可回收对象。
- 被判定为不可达的对象要成为回收对象,要至少经历两次标记过程。
- 如果在这两次标记过程中仍然没有逃脱成为可回收对象的可能性,则基本上就真的成为可回收对象了。
通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称为“引用链”,当一个对象到 GC Roots 没有任何的引用链相连时(从 GC Roots 到这个对象不可达)时,证明此对象不可用。
(3)什么是GC Root
-
指一些被JVM认为是存活的对象,它们是垃圾回收算法的起点
-
可以理解为由堆外指向堆内的引用, 本身是没有存储位置,都是字节码加载运行过程中加入 JVM 中的一些普通引用
-
通俗的例子可以是一个树形结构,树的根节点就是GC Roots
-
是垃圾回收器的起点,如果一个节点没有任何子节点与根节点相连,那这个节点就被认为是不可达的,可以被回收器回收
-
通俗的例子
- 将GC Roots比喻成一座城市,城市中有很多建筑物,这些建筑物就是内存中的对象
- GC Roots就像城市的卫生局、消防局等,它们直接或间接地与城市中的建筑物相连
- 从这些机构出发,通过道路、桥梁等连接,最终能够到达所有的建筑物
- 如果一个建筑物没有与这些机构相连,那么它就被认为是废弃的,可以被清理掉
(4)JVM中的GCRoots对象有哪几种
-
虚拟机栈(栈帧中的本地变量表)中引用的对象。
-
方法区中类静态属性引用的对象
- JDK 1.7 开始静态变量的存储从方法区移动到堆中
- 比如你定义了一个static 的集合对象,那里面添加的对象就是可以被GC Root可达的
-
方法区中常量引用的对象
- 字符串常量池从 JDK 1.7 开始由方法区移动到堆中
-
本地方法栈中JNI(即一般说的Native方法)引用的对象。
(5)对象可回收,就一定会被回收吗?
- 不一定会回收,对象的finalize方法给了对象一次最后一次的机会。
- 当对象不可达(可回收)并发生 GC 时,会先判断对象是否执行了 finalize 方法,如果未执行则会先执行 finalize 方法
- 将当前对象与 GC Roots 关联,执行 finalize 方法之后,GC 会再次判断对象是否可达
- 如果不可达,则会被回收,如果可达,则不回收!
- 需要注意的是 finalize 方法只会被执行一次,如果第一次执行 finalize 方法,对象变成了可达,则不会回收
- 但如果对象再次被 GC,则会忽略 finalize 方法,对象会被直接回收掉!
未完待续。。。。。。,今个累累,改天再补上。