zoukankan      html  css  js  c++  java
  • GC垃圾回收算法

    GC垃圾回收算法

    GC的意思是java垃圾回收,主要由两个步骤组成

    1. 使用某种算法寻找需要回收的对象
    2. 使用某种算法回收垃圾

    垃圾回收主要针对的是堆中的对象,有变量引用的对象表示是存活的,没有一个变量引用的对象就是需要回收的对象。

    寻找需要回收的对象

    1.引用计数法

    引用计数法原理很简单,就是有对象引用你,数就加一,当对象不再引用你的时候,数就减一。当数为0的时候就可以回收。

    无法解决循环引用问题

    循环引用就是 A -> B B -> A

    这样的情况如果想释放A,就得B要向A发起release请求,而B要向A发起release请求,就得执行deallocate,要执行deallocate就得收到A向B发出的release请求,双方就都等待对方的release请求而导致陷入死循环,只要对象持有关系在形成环就存在了循环引用的问题,所以JVM不使用引用计数法。

    image-20210115214545340

    引用计数法效率高,但是无法解决循环引用的问题,而且每个对象都要有一个计数器,增加了程序执行的成本。

    2. 根搜索算法

    根搜索算法,是有被标记为GCroots的对象出发,不断地寻找他们的引用节点,寻找并标记所有的存活的对象。

    GCroots对象包括:

    1. 栈帧中的本地变量表
    2. 方法区中的常量引用的对象
    3. 本地方法栈中JNI(Native方法)的引用对象

    没有被根搜索算法标记的对象就是可回收的对象。

    注意点

    1. 根搜索算法开始标记之前,要暂停应用线程 stop the world,因为程序如果是活动的,那么变量和对象之间的引用关系是不确定的,那么无法正确的标记哪些对象是存活的,所以出发STW,让JVM去回收垃圾称之为安全点。
    2. stw时间的长短取决于存活对象的多少
    3. 第一次遍历对象图没有被标记的对象,会被放入一个队列中,稍后GC会对这些对象进行一次小规模的标记,如果还是没有变量引用这些对象就会被回收掉
    4. 判断对象可达是根据的是强引用

    回收垃圾对象的算法

    标记清除算法

    使用根搜索算法得到不需要的对象之后,直接清理掉对象所在的内存空间,并把这块内存放入空闲表。缺点就是多次标记清理算法之后内存中的碎片越来越多,之后无法分配大对象。如果无法分配只能再次出发GC操作。

    标记整理算法

    把所有存活的对象往一个方向移动,这样就不会有内存碎片产生,缺点就是GC的时间会增长,因为需要移动存活的对象,并更新他们的引用地址。

    复制算法

    该算法把内存空间分为两块,一块对象面用于分配新的对象,一块空闲面是在触发GC之后,把存活的对象都移到空闲面上,把对象面的内存一次性都清除掉。这样之后对象面和空闲面就互换了。

    优点是 标记和复制可以同时进行,每次对整块内存进行回收,效率高。只需要移动栈顶指针重新分配内存。没有内存碎片。

    缺点是 存活对象很多的时候,需要的时间很多。分配新对象的空间减少了。

    java 堆内存

    java堆内存基于Generation算法,分为新生代,老生代和持久代。

    新生代

    新生代分为 eden和survivor0和survivor1区 内存空间比例按照8:1:1比例分配。

    新对象放在 eden区,如果eden区满了以后就会触发scavenge GC 把 eden区存活的对象放在survivor0中,清空eden区,如果survivor0区也满了,就把eden 和 suivivor0区存活的对象放入survivor1 区,如此反复,当survivor1区的对象也满了,就会在存活对象中寻找年龄 > 15(在一次 scavenge GC存活下来的对象年龄 + 1)的对象,把它放入老年代。

    新生代使用的是复制算法

    年老代

    老年代存放在年轻代中经过15次GC之后存活的对象,老年代对象也满了之后会触发FULL GC。由于年老代的内存空间是新生代的要大近2陪,所以大对象的分配一般都是直接分配到年老代。FULL GC会对三个年代的区域都进行回收。因此要减少FULL GC的次数。

    有如下原因可能导致Full GC:

    1. 年老代(Tenured)被写满;
    2. 持久代(Perm)被写满;
    3. System.gc()被显示调用;

    年老代由于存活的对象较多,所以不适合使用复制算法,一般是使用标记清除或者标记处理算法。根据具体的垃圾回收器来确定那种垃圾回收算法

    持久代

    持久代存放的是class文件,静态方法,常量还有JVM自己的反射对象等。永久代空间在Java SE8特性中已经被移除。取而代之的是元空间(MetaSpace)。元空间的大小受限于本地内存限制(32或者64位的虚拟内存大小)。FULL GC的时候就不会考虑到这块区域,不好的地方就是如果存在内存泄漏,元空间的内存会越来越大,导致机器内存不足。

    垃圾收集器

    有的是基于单线程的垃圾回收器,有的是基于多线程的垃圾回收器,不同的垃圾回收器可能基于不同的垃圾回收算法。

    内存泄漏

    静态变量是不会被垃圾回收器回收的,当hashmap,list等集合作为静态变量,往里面添加的所有对象都不会被回收。从而导致内存泄漏。

    Static Vector v = new Vector();
    
    for (int i = 1; i<100; i++)
    
    {
    
        Object o = new Object();
    
        v.add(o);
    
        o = null;
    
    }
    

    这个V对象和100个o对象都不会被回收,从而导致内存泄漏。

    还有各种连接使用完了未关闭就会导致内存泄漏,比如数据库连接,网络连接,IO连接没有使用close方法,就不会被GC回收导致内存泄漏。

  • 相关阅读:
    CMD 已存在的表, 没有主键的 添加主键属性
    回调函数 call_back
    在Ubuntu下安装MySQL,并将它连接到Navicat for Mysql
    F查询和Q查询,事务及其他
    Djabgo ORM
    Diango 模板层
    Django视图系统
    Django简介
    Web 框架
    HTTP协议
  • 原文地址:https://www.cnblogs.com/iandf/p/14745516.html
Copyright © 2011-2022 走看看