深入Java虚拟机GC回收机制

本章章节目录:

1、运行时有哪些内存区域?

2、运行时怎么给类、对象分配内存?

3、哪些区域的内存需要回收?

4、内存中的哪些对象可以回收?  

5、如何回收?

一、运行时有哪些内存区域?

     可以参看上述文章:

     http://www.dczou.com/viemall/237.html

二、运行时怎么给类、对象分配内存?

     要了解java垃圾回收机制前必须知道java怎么分配给对象内存的,根据上面运行时数据区域的划分可以知道,几乎所有的对象都在堆上分配,

     而类信息、常量、静态变量在方法区分配。堆内存是分代管理的,对象优先在Eden分配;大对象(所谓的大对象是指需要连续内存空间的java对象,

      如很长的字符串或者数组)直接进入老年代;长期存活的对象将进入老年代,在垃圾回收时在Survivor中每熬过一次youngGC,他的年龄就增加1,直到到达指定的年龄就会被放入老年代。

三、那些区域的内存需要回收?

    根据运行时数据区域的各个部分,程序计数器、虚拟机栈、本地方法栈三个区域随着线程而生,随线程灭而灭。栈中的栈帧随着方法的进入和退出而进栈出栈。每个栈帧分配多少内存在类结构确定下来的时候就基本已经确定。所以这个三个区域内存回收时方法或者线程结束而回收的,不需要太多关注;而java堆和方法区则不一样,一个接口不同实现类,一个方法中不同的分支,在具体运行的时候才能确定创建那些对象,所以这部分内存是动态的,也是需要垃圾回收机制来回收处理的。

四、内存中的哪些对象可以回收?

   1、堆内存:判断堆内的对象是否可以回收,要判断这个对象实例是否确实没用,

           判断算法有两种: 引用计数法和根搜索算法。

       引用计数法:

           就是给每个对象加一个计数器,如果有一个地方引用就加1,当引用失效就减1;当计数器为0,则认为对象是无用的。这种算法最大的问题在于不能解决相互引用的对象,如:A.b=B;B.a=A,在没有其他引用的情况下,应该回收;但按照引用计数法来计算,他们的引用都不为0,显然不能回收。

      根搜索算法:

           这个算法的思路是通过一系列名为“GC Roots”的对象作为起点,从这个节点向下搜索,搜索所经过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(图论的不可达)时,则证明该对象不可用。

           java等一大部分商用语言是用根搜索算法来管理内存的,java中可以做为GC Roots的对象有如下几种:

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

    方法区中的类静态属性引用的对象;

    方法区中常量引用的对象;

    本地方法栈JNI(Native)的引用对象;

           如果进行垃圾回收的时候发现一个对象没有在GC Root链上,那么就需要进行两次的标记过程,如果当前发现没有关联在GC Root链上,那么就会进行第一次标记并且进行筛选,筛选的条件是此对象是否对象的finalize()方法被覆盖或该方法已经被虚拟机调用过。

          如果对象判断有必要执行,则此时该对象会被放入”即将回收“集合,否则就会放入F-Queue的对象中等待执行finalize()方法,如果在此方法中对象将自己与GC Root链上的任何一个对象关联,

那么就会被移除”即将回收“集合。

public class CanReliveObj {
      public static CanReliveObj obj;
      @Override
      protected void finalize() throws Throwable {
          super.finalize();
          System.out.println("CanReliveObj finalize called");
          obj=this;
      }
      @Override
      public String toString(){
          return "I am CanReliveObj";
      }
 
      public static void main(String[] args) {  
           obj=new CanReliveObj();
           obj=null;   //可复活
           System.gc();
           Thread.sleep(1000);
           if(obj==null){
               System.out.println("obj 是 null");
           }else{
               System.out.println("obj 可用");
           }
           System.out.println("第二次gc");
           obj=null;    //不可复活
           System.gc();
           Thread.sleep(1000);
           if(obj==null){
           System.out.println("obj 是 null");
           }else{
           System.out.println("obj 可用");
    }
}
CanReliveObj finalize called
obj 可用
第二次gc
obj 是 null

总结:

   经验:避免使用finalize(),操作不慎可能导致错误。

   优先级低,何时被调用, 不确定、 何时发生GC不确定

   可以使用try-catch-finally来替代它


   2、方法区:方法区回收主要有两部分:废弃的常量和无用的类。废弃的常量判断方法和堆中的对象类似,只要判断没有地方引用就可以回收。相比之下,判断一个类是否无用,条件就比较苛刻,需要同事满足下面3个条件才能算是“无用的类”:

该类的所有实例都已经被回收,也就是java堆中不存在该类的任何实例;

   加载该类的ClassLoader已经被回收;

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

   虚拟机可以对于满足上面三个条件的无用类进行回收,仅仅是可以回收,具体能否回收,JVM提供了-Xnoclassgc参数进行控制。

   无论是通过引用基数判断对象的数量,还是通过根搜索算法判断对象的引用链是否可达,判定的对象是否存活都与“引用”有关,对于Java对引用概念的分析可以参看http://www.dczou.com/viemall/122.html  

五、如何回收?

    gc有多种算法,根据不同的算法实现了不同的垃圾回收器,每种收集器在可以在不同的应用场景使用。

  回收算法:

      标记-清除(Mark-Sweep)算法:如它的名字一样,算法分“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉被标记的对象。主要有       两个缺点:一个是效率问题,标记和清除效率都不高;另一个是空间问题:标记清除后会产生大量空间碎片。

blob.png

    复制(Copying)算法:将可用内存按容量分成大小相等的两块,每次只使用其中一块,当这块内存使用完了,就将还存活的对象复制到另一块内存上去,然后把使用过的内存空间一 次清理掉。这样使得每次都是对其中一块内存进行回收,内存分配时不用考虑内存碎片等复杂情况,只需要移动堆顶指针,按顺序分配内存即可,实现简单,运行高 效。这种方法适用于短生存期的对象,持续复制长生存期的对象由于多次拷贝,导致效率降低。优点是实现简单,运行效率高,缺点是内存缩小为原来的一半。

blob.png

    标记整理(Mark-Compact)算法:此算法仍然与标记-清除算法一样,第一步标记,第二步不是对无用对象清理,而是,让所有可用对象都向一端移动,然后直接清理掉端边界以外的内存。标记整理算法的优点是不会产生空间碎片。

blob.png

    分代收集(Generation Collection)算法:分代收集算法根据对象存活周期的不同将内存划为几块,一般把java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最合适的收集算法。在新生代中,每次垃圾回收时都发现大批对象死去,只有少量存活,那就选用复制算法,付出少量复制成本就可以完成收集。而老年代中对象存活率较高且没有空间进行担保(后面讲新生代的担保分配),就必须使用“标记-清除”或者“标记-整理”算法。

   无论是 web server ,还是 service server ,反正就是无法避免 full GC 的 stop-the-world,轻则 503 ,重则服务恢复之后的雪崩。所以必须知道JVM优化配置及GC机制,Java代码上的优化。

   下章将详细分析GC回收器,更好的理解JVMGC回收配置。

参考文章:

  http://alaric.iteye.com/blog/2262566?utm_source=tuicool&utm_medium=referral

   JVM性能调优参考资料: 《深入理解Java虚拟机》-周志明

发表评论