zoukankan      html  css  js  c++  java
  • jvm垃圾回收机制

    在利用java语言写程序最爽的一点估计就是程序员基本不用考虑废弃对象的释放问题,因为在java程序中有垃圾回收机制帮我们管理内存(下面称GC,gabbage collection),垃圾收集器会在适当的时候对没用的对象进行整理与回收。下面简单整理下jvm 中GC相关知识点。

    一、如何判定对象可被回收?

      1、引用计数法。

        这种算法的大概思路如下:当对象被创建时,GC程序会存储有多少个引用指向该对象,当没有引用指向该对象时,表示该对象已经不可用,则可以对其进行回收。这种算法相对来说简单粗暴,但是,它不能解决对象之间引用循环指向的问题,如下丑图:

    当对象之间有引用互相指向时,引用计数发并不能确保没用对象能被回收。

      2、可到达性分析算法。

        可到达分析算法依据这样一种思想:所有可用的对象,都会通过某条路径关联到一个个根部的元素,如果从根部元素出发无法到达某些元素(可以类比为图的模型),则认为这些元素已经没有任何相关的线程或者方法区数据和它相关了,这时候,可以认为它们是没用的了。可到达分析算法有效地解决了对象之间相互引用的问题,它依靠的准则是,只要某个对象对程序来说是有用的,必定可以通过根部元素搜索出来(必定和根部节点位于同一个图模型内)。大概结构图看下面丑图:

    可到达算法保证了不被利用的对象的有效回收,但是该算法较为复杂,在如何快速确定对象是否和根元素连接上需要做很多额外的工作。

      3、对象可以被回收的准则总结

        A、对于堆中的对象而言,就直接通过可到达算法进行判断即可;

        B、但是,对于方法区中的类对应数据(例如静态变量对象,class对象等),如果需要回收,一般判断是比较严格的,需要满足一下三个条件:

          a、该类的所有对象已经被回收

          c、该类的类加载器已经被回收

          d、该类的class对象没有在任何地方被引用(最典型的就是程序中没有其他正在运行的代码会用到反射机制)

     二、如何进行内存空间的回收?

      在确定了哪些对象可以被回收时,我们还要考虑应该如何进行对象的回收,一般现代的虚拟机有以下的回收算法,他们的具体适用场景也不一样,下面进行简单的整理归纳。

      1、标记清除算法。

        该算法直接对要回收的对象进行标记,在GC执行时,直接将标记的对象进行回收。这个算法相对来说实现起来比较简单,执行起来速度也快。但是,由于要回收的对象之间的内存块是不连续的,在进行回收之后,会造成很多空间碎片,不便于大对象的创建。

      2、复制算法。

        复制算法的做法和标记整理的算法不同,它是直接将还有用的对象复制到另一个区域,剩下的就是无用的对象,可以直接被回收。复制算法针对的情形是:回收的对象远远多于存活的对象,这种情况在大多数的新生代区域(该区域所有对象都没有经历过垃圾回收)是满足的。

        一般来说,虚拟机会将堆中的内存划分为8:1:1(这个比例的确定是因为大多数时候在新生代进行垃圾回收时,回收对象和存活对象大小之比大概为9:1)的大小区域,用于执行垃圾回收算法,一般将它们分别命名为eden和survior区域,看下面示意图:

    在上面的内存划分示意图中,eden区域存储的是刚刚new出来的对象(没有经历过垃圾回收),而survior中的一个区域是作为上一次(或更早之前)存活对象的场所,另一个区域是用作回收时的中间站。在进行GC时,会将eden和相应的survivor区域中的存活对象统一复制到另一个作为中间站的survivor中,然后清除剩余的对象,重复这个过程。(当然,survior中的存活对象有可能会不断增多,这是,当有些存活对象年龄足够大时,会被移到老年区,老年去相对与新生代区域垃圾回收的频率会低很多。)当然,如果在新生代出现了存活对象大于10%的情况(例如在某个时刻有一个很大的对象,它在第一次GC时不能进行回收)时,survivor中的内存不够用了,GC程序会提前让对象进入老年区(也就是会采用老年代的区域进行对象的复制),以保证内存空间的充足。所以,很多时候,大对象会直接进入老年区,这样可以防止大对象的频繁复制损耗性能。

      显然,复制算法有效解决了内存碎片化的问题,但是,复制对象针对的是存活对象一般保持在10%左右的新生代区域罢了,在老年代区域中,存活对象会更多,并不适合采用该算法,一般会采取下面介绍的标记整理算法。

      3、标记整理算法。

        上面也说到 了,标记整理算法主要针对的是老年区域的对象。它采取的方式是标记存活对象,然后让存活的对象向某一端移动,GC时,直接清除端边界之外的对象。如下示意图:

      总的来说,在新生代采用的回收方法是复制算法,在老年代采用的是标记清除或者标记整理的算法进行对象的回收。

    OK,垃圾回收机制的大概总结就这么多,至于垃圾回收算法的具体实现思想,下一篇博客会简单总结。

  • 相关阅读:
    .NET开发人员遇到Maven
    基于VS Code创建Java command-line app
    IntelliJ IDEA连接TFS local workspace无法正常签入
    Xcode连接TFS Git用户名和密码不正确解决方案
    Fiddler如何捕捉DefaultHttpClient的HTTP请求
    IIS 6的日志time-taken字段没有值的解决方案
    简单的音乐轮播JS
    SpringCloud分布式开发理解
    SpringCloud分布式开发五大神兽
    socket长连接和短链接区别
  • 原文地址:https://www.cnblogs.com/lcplcpjava/p/6645195.html
Copyright © 2011-2022 走看看