java虚拟机的认识(三)~垃圾回收机制算法分析

垃圾回收机制算法分析

 

什么是垃圾回收机制:

参考:https://zhidao.baidu.com/question/1610477166608068867.html

1、回收的内容是new出来的对象所占内存;对象在内存中的状态有可达状态、可恢复状态、不可达状态,当处于可恢复状态时可能会进行垃圾回收。
2、垃圾回收只与内存有关,清理内存外的资源依靠finalize()方法。垃圾回收机制在回收某个对象的内存前会调用该对象的finalize()方法。 

3、强制系统垃圾回收的方式(通过程序通copy知系统进行垃圾回收,但系统还不一定进行垃圾回收): System.gc(); Runtime.getRuntime().gc();

4、垃圾回收以单独的线程在后台运行,为减少虚拟机额zd外消耗,一般在内存不足时会进行垃圾回收,所以就算强制系统垃圾回收,垃圾回收也不一定发生; 5

对于上面的参考说明 我进一步详细的说明下:

可达对象 / 不可达对象:

  在Java中,是通过可达性分析(Reachability Analysis)来判定对象是否存活的。该算法的基本思路就是通过一些被称为引用链(GC Roots)的对象作为起点,从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到GC Roots没有任何引用链相连时(即从GC Roots节点到该节点不可达),则证明该对象是不可用的。

如上图所示,object1~object4对GC Root都是可达的,说明不可被回收,object5和object6对GC Root节点不可达,说明其可以被回收。

在Java中,可作为GC Root的对象包括以下几种:
  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI(即一般说的Native方法)引用的对象
不定时去堆内存中清理不可达对象。不可达的对象并不会马上就会直接回收
垃圾收集器在一个Java程序中的执行是自动的,不能强制执行,
即使程序员能明确地判断出有一块内存已经无用了,是应该回收的,
程序员也不能强制垃圾收集器回收该内存块。
程序员唯一能做的就是通过调用System.gc 方法来"建议"执行垃圾收集器,
但其是否可以执行,什么时候执行却都是不可知的。这也是垃圾收集器的最主要的缺点。
当然相对于它给程序员带来的巨大方便性而言,这个缺点是瑕不掩瑜的。
public class Test {
    public static void main(String[] args) {
        Test test = new Test();
        test = null;
        System.gc(); // 手动回收垃圾
    }

    @Override
    protected void finalize() throws Throwable {
        // gc回收垃圾之前调用
        System.out.println("垃圾回收机制...");
    }
}
View Code

 

finalize()方法最终判定对象是否存活:

  Java技术使用finalize()方法在垃圾收集器将对象从内存中清除出去前,做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在Object类中定义的,因此所有的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。

  即使在可达性分析算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历再次标记过程。
标记的前提是对象在进行可达性分析后发现没有与GC Roots相连接的引用链。
1. 第一次标记并进行一次筛选。
  筛选的条件是此对象是否有必要执行finalize()方法。
  当对象没有覆盖finalize方法,或者finzlize方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”,对象被回收。
2. 第二次标记
  如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个名为:F-Queue的队列之中,并在稍后由一条虚拟机自动建立的、低优先级的Finalizer线程去执行。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束。这样做的原因是,如果一个对象finalize()方法中执行缓慢,或者发生死循环(更极端的情况),将很可能会导致F-Queue队列中的其他对象永久处于等待状态,甚至导致整个内存回收系统崩溃。
  Finalize()方法是对象脱逃死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模标记,如果对象要在finalize()中成功拯救自己----只要重新与引用链上的任何的一个对象建立关联即可,譬如把自己赋值给某个类变量或对象的成员变量,那在第二次标记时它将移除出“即将回收”的集合。如果对象这时候还没逃脱,那基本上它就真的被回收了。

作者:kylinxiang
链接:https://www.jianshu.com/p/8f5fa8288d9b
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

新生代与老年代

  Java 中的堆是 JVM 所管理的最大的一块内存空间,主要用于存放各种类的实例对象。

  在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。

  这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。

堆的内存模型大致为:

下图来自:https://blog.csdn.net/qq_19734597/article/details/80958817

  默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 ),即:新生代 ( Young ) = 1/3 的堆空间大小。老年代 ( Old ) = 2/3 的堆空间大小。其中,新生代 ( Young ) 被细分为 Eden 和 两个 Survivor 区域,这两个 Survivor 区域分别被命名为 from 和 to,以示区分。

  默认的,Edem : from : to = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定 ),即: Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。

  1. 根据垃圾回收机制的不同,Java堆有可能拥有不同的结构,最为常见的就是将整个Java堆分为
  2. 新生代和老年代。其中新生带存放新生的对象或者年龄不大的对象,老年代则存放老年对象。
  3. 新生代分为den区、s0区、s1区,s0和s1也被称为from和to区域,他们是两块大小相等并且可以互相角色的空间。
  4. 绝大多数情况下,对象首先分配在eden区,在新生代回收后,如果对象还存活,则进入s0或s1区,之后每经过一次
  5. 新生代回收,如果对象存活则它的年龄就加1,对象达到一定的年龄后,则进入老年代。

 

posted @ 2020-04-19 00:53  An-Optimistic-Person  阅读(88)  评论(0)    收藏  举报