java内功 ---- jvm虚拟机原理总结,侧重于GC

参考资料:《深入理解java虚拟机》、《thinking in java》、《Effective Java》

 

直接从最要紧的地方讲,Java GC算法。需说明一点,GC机制只是涉及堆内存的。因为堆内存是动态的,在程序运行期间分配的。

 

一.判断什么需要被回收:

 

1.引用计数法

           问题在于两个对象互相引用,然后各种指向null。堆内存中的对象是不会被回收的。

《java内功 ---- jvm虚拟机原理总结,侧重于GC》

       上图是体现一种变化,绘画技巧不行,用绿框表示对象成员,两个对象互相指向。

       依据引用计数法,当a=null,b=null。A、B不会被回收内存,因为他们互相引用。

 

2,可达性分析算法

       为了解决引用计数的缺陷,引入可达性分析算法。

       GC Root:

              1,虚拟机栈(栈帧中的本地变量表)中引用的对象。

              2,方法区中类静态属性引用的对象。

              3,方法区中常量引用的对象。

              4,本地方法栈中JNI(即一般说的Native方法)引用的对象。

        对于之前的例子。当a、b(栈中的变量)指向A、B(堆中的变量)时 ,A与B是GC Root。当a=null、b=null。A与B不再是GC Root,虽然A与B相互引用,但没有与其它任何GC Root可达。所以A与B,会被回收。

 

3.方法区的回收

        永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类。废弃常量的回收与对象的回收是一个道理。

         重点谈废弃的类需满足的几点:

                 1,该类的所有实例都已被回收

                 2,加载该类的ClassLoader已经被回收

                 3,该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问到该方法

 

二,垃圾收集算法:

 

1.标记-清除

         分为,标记和清楚两个阶段,想想便知道,会留下很多内存碎片。

2.复制算法

          将内存分为8:1:1。8的部分为Eden,1的部分为Survivor。

          首先是使用Eden部分内存,按照1的标记-清除算法来,做垃圾收集,自然会产生碎片。当Eden部分内存不够时,就将活着的对象都放入一个Survivor。此时Eden为空。继续分配对象内存、同时也就继续回收。这时那个装载着存活对象的survivor也在做着垃圾回收。当Eden或这个survivor内存不够用的时候,便将二者内存里活着的对象都放入另一个surviror。如此循环往复。

3.标记-整理

           与标记-清除相对,是一种标记后不直接清除,而是将所有活着的对象像一端移动,然后直接清理掉端边界以外的内存。

 

           不同场合应当使用不同的垃圾回收算法,当前商业虚拟机的垃圾回收都采用“分代收集”算法。新生代死亡率高,采用复制算法,老年代存活率高,采用标记-清除或者标记-整理。

 

三,实用性建议

        最后我们看下《Effective Java》一书给出的实用性建议:

                 1.消除过期的对象引用

                       了解了上述判断对象是否该回收的内容后,你就知道,如果堆内存里的对象被栈内存的变量所引用时,这块对象内存是不会被回收的。这便是java的内存泄漏。其逻辑在于,虽然Java的GC很有效,但一个应当释放的堆一直被你引用着,GC程序是没法给它判死刑的。

                 2.避免使用终结方法

                       终结方法(finalizer)通常是不可预测的,也是很危险的,一般情况下是不必要的。使用终结方法会导致行为不稳定,降低性能,以及可移植性问题。

 

                 这两点结合起来看,就是java不像C++有好用的delete方法来释放对象内存,java需要依赖GC,但是java要保证及时释放引用,以保证不会内存泄漏。

   

    原文作者:sonn
    原文地址: https://www.cnblogs.com/rixiang/p/5801214.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞