一、如何判断对象可以回收
1、引用计数法
只要这个对象被其他变量所引用,就让对象的引用计数+1。如果引用了2次,就让计数变成2,如果不引用了,-1。
【注】循环引用的情况
如下图所示的循环引用。各自的引用计数均为1。虽然都不会再使用了,由于引用计数不是0,所以不能进行垃圾回收,引起内存泄漏。
2、可达性分析算法
首先确定根对象(根对象肯定不能当做垃圾回收的对象)
- Java 虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象
- 扫描堆中的对象,看是否能够沿着 GC Root 对象 为起点的引用链找到该对象,找不到,表示可以回收
- 哪些对象可以作为 GC Root ?
- 虚拟机栈中的局部变量表所引用的对象
- 方法区中的类静态属性引用和常量引用对象
- 本地方法栈中所引用的对象
【演示GC Root】
List1是一个引用,存在于活动栈帧里面,属于局部变量。
new ArrayList这一段是存储在堆里面的
在当前活动线程中,局部变量所引用的对象可以作为根对象
执行了list1=null这一句后, 现在已经没了ArrayList这个对象,live这个参数就会自动执行一次垃圾回收。
【注】
Jmap:查看堆内存的占用情况
Jamp-dump:将堆内存运行时的状态转储成一个文件
format:格式
b:二进制
live:表示会主动触发一次垃圾回收
file:表示抓取后的快照存放在哪个文件下
21384:进程编号
3、关于垃圾回收的四种引用
实线代表强引用,虚线代表其他的几种引用
(1)强引用
只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收
(2)软引用
-
【前提】:没有强引用引用当前的这个软引用对象
-
仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用对象
-
可以配合引用队列来释放软引用自身
- 在创建这个软引用对象时,创建了一个引用队列,那么软引用所引用的对象被垃圾回收后,软引用自身就会进入引用队列
- 若要释放软引用自身的内存空间,通过引用队列来找到并进行释放。
【演示软引用】
List对softReference是强引用,而softReference对于byte[ ]属于软引用
前面一种属于强引用,会导致内存溢出。
使用软引用的好处:当内存紧张时,首先会触发minor GC,小规模的进行垃圾回收,回收年轻代,在进行下一次操作,如果现在仍然内存紧张,并且触发miner GC后,仍然不会有改观,就会触发full GC,进行大规模的垃圾回收。
针对当前代码,就是前4个被软引用的对象都被垃圾回收了,只剩下第五个了
【演示软引用,配合引用队列】
当前为null的软引用本身可以进行清除,需要使用引用队列。
使用了垃圾回收后,软引用本身会进入引用队列。
要将这部分清楚,得到的结果就是没有null值的。
(3)弱引用
- 仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象
- 可以配合引用队列来释放弱引用自身
【演示弱引用】
进行到第10次时,直接调用了一次full GC,只剩下最后一个了。之前的和软引用类似。
(4)虚引用
必须配合引用队列使用,主要配合 ByteBuffer 使用。被引用对象回收时,会将虚引用入队,由 Reference Handler 线程调用虚引用相关方法释放直接内存
调用cleanner方法,根据直接内存的地址,调用unsafe对象的freeMemory方法
(5)终结器引用
无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 finalize方法,第二次 GC 时才能回收被引用对象
不推荐,考虑情况较多,效率比较低
学自黑马程序员,加上自己的理解和整理,侵删