如何判断垃圾是否可以回收?
1、引用计数法(早期的Python)
原理:
只要该对象被其他的对象所引用,对象就计数加一,若被引用两次,就计数加二,当引用该对象的变量不再引用该对象的时候,计数减一。当计数为零的时候,代表没有变量对该对象进行引用,最后就会作为垃圾回收。
缺点:
当发生循环引用的时候,当A,B都不再使用的时候,计数仍为一,不会被垃圾回收。
public void buidDog(){
Dog newDog = new Dog()
Tail newTail = new Tail();
newDog.tail = newTail;
newTail.dog = newDog;
}
在代码中,newTail中拿着对newDog的引用,newDog中拿着对newTail的引用。若使用引用计数法,则其对象基数都为一,而运行buidDog后,无法进行垃圾回收(引用计数不为零)
2、可达性分析算法(java)
原理:通过一系列的称为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连(用图论的话说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。
注:GC根本身不是对象,而是对对象的引用。GC根引用的任何对象都将在下一个垃圾收集中自动生存
可以作为GC 根对象的对象
JAVA中有四种主要的根:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象:当前正在运行的方法中的局部变量被视为GC根。这些变量引用的对象始终可以通过在声明的方法中立即访问,因此必须保留它们。这些根的生命周期取决于程序的构建方式。在调试生成中,只要方法在堆栈上,局部变量就一直存在。
- 方法区中的类静态属性引用的对象:它们引用的对象可以由声明它们的类随时访问(如果它们是公共的,则可以由程序的其他部分访问)
- 方法区中的常量引用的对象
- 本地方法栈JNI(即一般说的Native方法)中的引用的对象:包括系统的核心进程,同步锁机制(正在加锁的对象)等等
java的四种引用
1、强引用
沿着根节点GC链可以直接找到。都是强引用。
强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。如下:
Object o = new Object(); // 强引用
当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。如果不使用时,要通过如下方式来弱化引用,如下:
o = null; // 帮助垃圾收集器回收此对象
显式地设置o为null,或超出对象的生命周期范围,则gc认为该对象不存在引用,这时就可以回收这个对象。具体什么时候收集这要取决于gc的算法。
2、软引用
没有被直接的强引用所引用,在垃圾回收后,内存依然不足,则软引用会被当做垃圾全部回收。如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。
应用:软引用可用来实现内存敏感的高速缓存。比如说图片,视频缓存,浏览器的前进按钮等的
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;
/**
* 演示软引用, 配合引用队列
*/
public class Demo2_4 {
private static final int _4MB = 4 * 1024 * 1024;
public static void main(String[] args) {
List<SoftReference<byte[]>> list = new ArrayList<>();
// 引用队列
ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
for (int i = 0; i < 5; i++) {
// 关联了引用队列, 当软引用所关联的 byte[]被回收时,软引用自己会加入到 queue 中去
SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB], queue);
System.out.println(ref.get());
list.add(ref);
System.out.println(list.size());
}
// 从队列中获取无用的 软引用对象,并移除
Reference<? extends byte