美文网首页
JAVA 垃圾回收机制

JAVA 垃圾回收机制

作者: 超人TIGA | 来源:发表于2020-12-17 21:06 被阅读0次

    垃圾回收之前,先来了解一下运行时数据是怎么存的。

    先来一个最简单的例子。

    public class Demo{
    
        public static void main(String[] args){
            System.out.println("Hello world");
        }
        
        private void methodA(){
            int x = 5;
            int y = 8;
            int z = x + y;
        }
    
        private void methodB(){
        }
    }
    

    这个demo,从运行到结束,都经历了什么流程,里面的数据都怎么处理了?其实,程序代码,都是由一个个线程跑起来的,main方法也不例外,启动一个线程,就会产生一个虚拟机栈。

    栈里面放的栈帧,就是方法method,而每一个方法里面都可以定义局部变量(int x = 5;),还可以设置返回值等。大致上如下图: image.png 还有其他的操作数栈,就是用于符合操作的,例如上面的int z = x + y,通过入栈和出栈,得到最终结果。

    动态链接,最常见的就是多态。
    返回地址,就是一个方法执行完,要返回到哪里继续执行。
    要注意,局部变量表存储的是8种基本类型,那对象类型呢?存哪里。

    private void methodB(){
        Object o = new Object();
    }
    

    其实方法里的对象变量,是存在堆里面的,但是局部变量表会有一个引用,指向堆里面的这个对象。

    排除在堆中的对象变量,上面的一系列东西,都是跟随线程的生命周期的,就是线程私有,一个线程有一份,是个线程有十分。

    接下来,我们看共享的部分,方法区和堆。这里就是线程共享的东西。这部分的数据,是共享的,跟线程的生命周期无关。
    放方法区的数据:字节码、静态变量、常量
    放堆的数据:对象

    既然这部分的数据跟线程的生命周期无关,也就是说,当线程全部逻辑结束了,方法区和堆里面的数据,也不会立刻被销毁。同时又因为这是共享的数据,如果要销毁回收,一定要先知道还有没有其他地方在使用。而这里就需要了解内存的分代思想了,也就是JMM。

    image.png 这就是JMM的内存模型。
    方法区:绿色的部分,jdk1.8以前是永久代,1.8以后是元空间,区别是什么呢?其实就是1.8以后,这一部分允许内存空间不连贯了。
    堆:2个蓝色的部分,即新生代+老年代,而新生代又分为3个,Eden区和2个交换区S0和S1,他们的大小比例是8:1:1。
    存放对象

    当我们存放对象的时候,一般会遵循一些规则:
    ①先存放到Eden区
    ②如果新对象的大小>Eden区的大小,可以直接存到老年代
    ③长期存活的对象进入老年代(经历15次垃圾回收)
    ④担保原则
    记住无论经过多少次垃圾回收,都只会在新生代和老年代这里,不会去到永久代或者元空间。元空间存放的是字节码、常量、静态变量的

    GC垃圾回收

    可达性分析算法:GCRoot(对象)、常量、静态变量
    检查引用链,判断对象的存活。
    如果对象存活,则不回收。而垃圾回收的过程,就是利用算法,对堆的清理和整理。

    复制回收算法
    image.png 把内存分为2个区,一个正在使用的,一个预留的,如果触发回收,则把使用区里面的不可回收对象,找出来并逐一放到预留区,然后把使用区擦除清空。(这时候,使用区和预留区就对换了,左边变成了预留区,右边变成了使用区)
    优点:简单高效,不会存在内存碎片,典型的空间换时间例子。
    缺点:内存利用率低,永远只有一半在使用,当存活的对象越来越多,效率就越来越低。
    标记清除算法
    image.png 标记清除也很好理解,分2步,标记和清除。
    ①扫描内存区,把可回收的对象标记
    ②把标记的对象进行回收
    优点:存活对象很多时,效率高
    缺点:碎片化,如果再有一个大的对象新建,由于没有足够的连续空间分配,会提前触发GC。
    标记整理算法
    image.png 标记整理则是:
    ①把不可回收的对象标记
    ②把不可回收的对象进行整理,排在内存的一端连续空间内
    ③调整边界的指针,然后把边界外的空间进行清理
    优点:标记清理算法的优化,避免了碎片的产生
    缺点:效率会相对慢
    那各个区分别用什么回收算法呢?

    Minor GC
    对新生代进行回收,不影响老年代。用的算法是复制回收算法,因为Eden区的对象,大部分都是生命周期很短的对象,用效率最快的算法。
    Full GC
    对整个堆进行回收,而其中的老年代和元空间,用的是标记清除和标记整理算法。由于是对整个堆进行回收整理,所以效率肯定相对慢,所以要尽量减少Full GC的次数。

    总结

    当我们不停地创建对象,就会先放到堆中的Eden区;
    ①如果Eden满了,就把存活的对象放到s0中;
    ②Eden和S0都满了,就触发Minor GC,利用复制回收算法,把存活对象整理到S1;
    ③交换区的S0和S1中的数据,每经历一次回收,计数+1,当经历过15次回收后还是存活,则会把这些对象转移到老年代。
    ④老年代被写满、主动调用System.gc则会触发Full GC,利用标记清除+标记整理算法进行回收。

    另外,如果一开始就用存活的对象,塞满Eden和S0,那就直接全部转到老年代,然后把新生代的空间都回收了。

    相关文章

      网友评论

          本文标题:JAVA 垃圾回收机制

          本文链接:https://www.haomeiwen.com/subject/zihzgktx.html