商品展示网站源码,上海网站备案审核,滨海网站建设找哪家好,wordpress商城源码JVM虚拟机垃圾回收机制垃圾回收机制判断是否存活算法引用计数法可达性分析法最终判定垃圾回收算法分代收集机制空间分配担保垃圾回收机制
判断是否存活算法
java语言和我们之前学的c/c不同#xff0c;c/c可以手动进行内存释放#xff0c;那样随时随地就可以释放不必要的内存…
JVM虚拟机垃圾回收机制垃圾回收机制判断是否存活算法引用计数法可达性分析法最终判定垃圾回收算法分代收集机制空间分配担保垃圾回收机制
判断是否存活算法
java语言和我们之前学的c/c不同c/c可以手动进行内存释放那样随时随地就可以释放不必要的内存减少内存溢出但我们java的内存是有jvm虚拟机自行释放的当一个对象不再被使用的时候jvm会自行释放并回收内存那么它是怎么样来判断一个对象是否存活的呢想必这里一定有着一套耐人寻味的算法
引用计数法
1·每个对象都包含一个引用计数器用于存放引用计数(其实就是存放被引用的次数) 2.每当有一个地方引用此对象时引用计数1 3·当引用失效比如离开了局部变量的作用域或是引用被设定为null时引用计数-1 局部变量的作用域从定义开始到其所在的大括号结束为止。 4·当引用计数为0时表示此对象不可能再被使用因为这时我们已经没有任何方法可以得到此对象的引用了 但这种方法却存在一个问题如果两个对象相互引用呢
public class jvm {public static void main(String[] args) {Test a new Test();Test b new Test();a.another b;b.another a;a b null;}private static class Test{Test another;}
}按照引用计数算法那么当出现以上情况时虽然我们无法在得到此对象的引用了并且此对象我们也无需再使用但是由于这两个对象直接存在相互引用的情况那么引用计数器的值将会永远是1但是实际上此对象已经没有任何用途了。所以引用计数法并不是最好的解决方案。
可达性分析法
首先每个对象的引用都有机会成为树的根节点(GC Roots)可以被选定作为根节点条件如下: 1.位于虚拟机栈的栈帧中的本地变量表中所引用到的对象其实就是我们方法中的局部变量)同样也包括本地方法栈中JNI引用的对象。 2.类的静态成员变量引用的对象。 3.方法区中常量池里面引用的对象比如我们之前提到的String类型对象。 4.被添加了锁的对象比如synchronized关键字) 5.虚拟机内部需要用到的对象。 一旦已经存在的根节点不满足存在的条件时那么根节点与对象之间的连接将被断开。此时虽然对象1仍存在对其他对象的引用但是由于其没有任何根节点引用所以此对象即可被判定为不再使用。比如某个方法中的局部变量引用在方法执行完成返回之后: 可以解释成当一个对象为null就是没有指向任何GC Roots则证明这个对象是不可以再使用的了 最终判定
我们在上面讲到的可达性分析法他只能告诉我们jvm可以去回收但真正的想回收还必须经过我们的最终判定并不是说算法判定可以回收之后这个对象一定会被回收 Object类里面有一个方法名为finalize()这个方法就是最终判定方法如果子类里面重写了此方法放该子类对象被判定为可回收的时候会进行二次确认如果确认之后对象依然不满足被回收的条件的时候那么该对象依然逃脱不了被回收的命运 示例代码
public class jvm {private static Test a;public static void main(String[] args) throws InterruptedException {a new Test();a null;System.gc();Thread.sleep(1000);System.out.println(a);}private static class Test{Overrideprotected void finalize() throws Throwable {System.out.println(this开启了他的);a this;}}
}运行结果 但切记该方法只会起到一次效果如果之后再出现这种情况就不会奏效了 示例代码
public class jvm {private static Test a;public static void main(String[] args) throws InterruptedException {a new Test();a null;System.gc();Thread.sleep(1000);System.out.println(a);a null;System.gc();Thread.sleep(1000);System.out.println(a);}private static class Test{Overrideprotected void finalize() throws Throwable {System.out.println(this开启了他的);a this;}}
}运行结果 之所有代码中要加上Thread.sleep()是因为finalize()方法是新开了一个线程执行的并且该线程的优先级比较低以免主线程已经执行完了该线程还在执行着 当然除了堆中的对象以外方法区中的数据也是可以被垃圾回收的但是回收条件比较严格这里就暂时不谈了 垃圾回收算法
前面我们介绍了对象存活判定算法现在我们已经可以准确地知道堆中的哪些对象可以被回收了那么接下来就该考虑如何对对象进行回收了垃圾收集器会不定期地检查堆中的对象查看它们是否满足被回收的条件。我们该如何对这些对象进行回收是一个一个判断是否需要回收吗?
分代收集机制
实际上如冤我们对堆中的每一个对象都依次判断是否需要回收这样的效率其实是很低的那么有没有更好地回收机制呢? 第一步我们可以对堆中的对象进行分代管理。 比如某些对象在多次垃圾回收时都未被判定为可回收对象我们完全可以将这一部分对象放在一起并让垃圾收集器减少回收此区域对象的频率这样就能很好地提高垃圾回收的效率了。 因此Java虚拟机将堆内存划分为新生代、老年代和永久代(其中永久代是HotSpot虚拟机特有的概念在JDK8之前方法区实际上就是采用的永久代作为实现而在JDK8之后方法区由元空间实现并且使用的是本地内存容量大小取决于物理机实际大小 这里我们主要讨论的是新生代和老年代。 不同的分代内存回收机制也存在一些不同之处在HotSpot虚拟机中新生代被划分为三块一块较大的Eden空间和两块较小的Survivor空间默认比例为8:∶ 1∶1老年代的GC评率相对较低永久代一般存放类信息等〈其实就是方法区的实现如图所示: Minor GC - 次要垃圾回收主要进行新生代区域的垃圾回收 -触发条件新生代Eden区容量已满的时候 Major GC -主要垃圾回收主要进行老年代的垃圾收集 Full DC -完全垃圾回收对整个java堆内存和方法区进行垃圾回收 ·触发条件1:每次晋升到老年代的对象平均大小大于老年代剩余空间 ·触发条件2: Minor GC后存活的对象超过了老年代剩余空间 ·触发条件3:永久代内存不足(JDK8之前) ·触发条件4∶手动调用System.gc(方法
空间分配担保
考虑一种极端情况正常情况下新生代的回收率是很高的所以不用太担心会经常出现这种情况就是当新生代的Eden区经历过一次回收之后仍然存在大量的对象。 这时就需要用到空间分配担保机制了可以把Survivor区无法容纳的对象直接送到老年代让老年代进行分配担保当然老年代也得装得下才行)在现实生活中贷款会指定担保人就是当借款人还不起钱的时候由担保人来还钱。 好那既然新生代装不下就丢给老年代那么要是老年代也装不下新生代的数据呢?这时老年代肯定担保人是当不成了那么这样的话首先会判断一下之前的每次垃圾回收进入老年代的平均大小是否小于当前老年代的剩余空间如果小于那么说明也许可以放得下(不过也仅仅是也许依然有可能放不下因为判断的实际上只是平均值万一这一次突然非常大呢)否则会先来一次FullGC进行一次大规模垃圾回收来尝试腾出空间再次判断老年代是否有空间存放要是还是装不下直接抛出0OM错误摆烂。