03.垃圾回收

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/masteryourself/article/details/84753054

1. 什么是垃圾回收机制

  • 不定时去堆内存中清理不可达对象。不可达的对象并不会马上就会直接回收, 垃圾收集器在一个 Java 程序中的执行是自动的,不能强制执行,即使程序员能明确地判断出有一块内存已经无用了,是应该回收的,程序员也不能强制垃圾收集器回收该内存块。程序员唯一能做的就是通过调用 System.gc 方法来"建议"执行垃圾收集器,但其是否可以执行,什么时候执行却都是不可知的。这也是垃圾收集器的最主要的缺点。当然相对于它给程序员带来的巨大方便性而言,这个缺点是瑕不掩瑜的。
1.1 finalize 方法作用
  • Java 技术使用 finalize() 方法在垃圾收集器将对象从内存中清除出去前,做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。 finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。
    public class Test01 {
    
        public static void main(String[] args) {
            Test01 test01 = new Test01();
            test01 = null;
            // 手动回收垃圾
            System.gc();
        }
    
        @Override
        protected void finalize() throws Throwable {
            // gc回收垃圾之前调用
            System.out.println("垃圾回收机制...");
        }
    
    }

2. 新生代与老年代

  • Java 中的堆是 JVM 所管理的最大的一块内存空间,主要用于存放各种类的实例对象。在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。
    这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。

在这里插入图片描述

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

  • 绝大多数情况下,对象首先分配在 eden 区,在一次新生代回收后,如果对象还存活,则会进入 s0 或者 s1 区,之后每经过一次新生代回收,如果对象存活则它的年龄就加 1,当对象达到一定的年龄后,则进入老年代。

3. 垃圾收集算法

3.1 引用计数法
  • 这是个比较古老而经典的垃圾收集算法,其核心就是在对象被其他所引用时计数器加 1,而当引用失效时则减 1,但是这种方式有非常重要的问题:无法处理循环引用的情况、还有就是每次进行加减操作比较浪费系统性能。
3.2 根搜索算法
  • 根搜索算法的基本思路就是通过一系列名为 “GC Roots” 的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链 (Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可用的。
3.3 标记清除法
  • 标记阶段:找到所有可访问的对象,做个标记

  • 清除阶段:遍历堆,把未被标记的对象回收

  • 该算法一般应用于老年代,因为老年代的对象生命周期比较长

  • 优点:可以解决循环引用的问题

  • 缺点:标记和清除的效率不高(尤其是要扫描的对象比较多的时候),会造成内存碎片(会导致明明有内存空间,但是由于不连续,申请稍微大一些的对象无法做到)

3.4 复制算法
  • 其核心思想就是将内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存留对象到未被使用的内存快中去,之后去清除之前正在使用的内存快中所有的对象,反复去交换俩个内存的角色,完成垃圾收集。

  • Java 中新生代和 from 和 to 空间就是使用这个算法

    1. 当 Eden 区满的时候,会触发第一次 young gc,把还活着的对象拷贝到 Survivor From 区;当 Eden 区再次触发 young gc 的时候,会扫描 Eden 区和 From 区域,对两个区域进行垃圾回收,经过这次回收后还存活的对象,则直接复制到 To 区域,并将 Eden 和 From 区域清空。

    2. 当后续 Eden 又发生 young gc 的时候,会对 Eden 和 To 区域进行垃圾回收,存活的对象复制到 From 区域,并将 Eden 和 To 区域清空。

    3. 可见部分对象会在 From 和 To 区域中复制来复制去,如此交换 15 次(由 JVM 参数 MaxTenuringThreshold 决定,这个参数默认是 15 ),最终如果还是存活,就存入到老年代

3.5 标记压缩法
  • 标记压缩法在标记清除法基础之上做了优化,把存活的对象压缩到内存一端,而后进行垃圾清理

  • Java 中老年代使用的就是标记压缩法

3.6 分代算法
  • 就是根据对象的特点把内存分成 N 块,而后根据每个内存的特点使用不同的算法。对于新生代和老年代来说,新生代回收频率很高,但是每次回收耗时都很短,而老年代回收频率较低,但是耗时会相对较长,所以应该尽量减少老年代的 GC
3.7 分区算法
  • 其主要就是将整个内存分为 N 多个小的独立空间,每个小空间都可以独立使用,这样细粒度的控制一次回收多少个小空间和哪些个小空间,而不是对整个空间进行 GC,从而提高性能,并减少 GC 的停顿时间

4. Minor GC 和 Full GC 区别

4.1 概念
  • 新生代 GC(Minor GC):指发生在新生代的垃圾收集动作,因为 Java 对象大多都具
    备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较快。

  • 老年代 GC(Major GC/Full GC):指发生在老年代的 GC,出现了 Major GC,经常
    会伴随至少一次的 Minor GC(但非绝对的,在 ParallelScavenge 收集器的收集策略里
    就有直接进行 Major GC 的策略选择过程)。Major GC 的速度一般会比 Minor GC 慢 10
    倍以上。

4.2 触发机制
  • 当年轻代满时就会触发 Minor GC,这里的年轻代满指的是 Eden 代满,Survivor 满不会引发 GCFull GC 触发机制

  • 当年老代满时会引发 Full GC,Full GC 将会同时回收年轻代、年老代,当永久代满时也会引发 Full GC,会导致 Class、Method 元信息的卸载其中

4.3 Minor GC 流程
  • 虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在 Eden 出生并经过第一次 Minor GC 后仍然存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为 1。对象在 Survivor 区中每熬过一次 Minor GC,年龄就增加 1 岁,当它的年龄增加到一定程度(默认为 15 岁)时,就会被晋升到老年代中。对象晋升老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold (阈值)来设置

5. 垃圾回收时的停顿现象

  • 垃圾回收器的任务是识别和回收垃圾对象进行内存清理,为了让垃圾回收期可以高效的执行,大部分情况下,会要求系统进入一个停顿的状态。停顿的目的是终止所有应用线程,只有这样系统才不会有新的垃圾产生,同时停顿保证了系统状态在某一个瞬间的一致性,也有益于更好地标记垃圾对象。因此在垃圾回收时,都会产生应用程序的停顿。

6. 对象进入老年代

  • 一般而言对象首次创建会被放置在新生代的 eden 区,如果没有 GC 介入,则对象不会离开 eden 区。一般来讲,只要对象的年龄达到一定的大小,就会自动离开年轻代进入老年代,对象年龄是由对象经历数次 GC 决定的,在新生代每次 GC 之后如果对象没有被回收则年龄加 1.虚拟机提供了一个参数来控制新生代对象的最大年龄,当超过这个年龄范围就会晋升老年代。

  • -XX:MaxTenuringThreshold:默认情况下为 15

  • -XX:PretenureSizeThreshold:对象超过指定大小即进入老年代,使用 PretenureSizeThreshold 可以进行指定进入老年代的对象大小,但是要注意 TLAB 区域优先分配空间

    public class Test05 {
    
    	public static void main(String[] args) {
    		//初始的对象在eden区
    		//参数:-Xmx64M -Xms64M -XX:+PrintGCDetails
            for(int i=0; i< 5; i++){
                byte[] b = new byte[1024*1024];
            }
    		
    		//测试进入老年代的对象
    		//
    		//参数:-Xmx1024M -Xms1024M -XX:+UseSerialGC -XX:MaxTenuringThreshold=15 -XX:+PrintGCDetails 
    		//-XX:+PrintHeapAtGC
    
    //		
    //		for(int k = 0; k<20; k++) {
    //			for(int j = 0; j<300; j++){
    //				byte[] b = new byte[1024*1024]; 
    //			}
    //		}
    		
    	}
    }
  • 根据设置 MaxTenuringThreshold 参数,可以指定新生代对象经过多少次回收后进入老年代。另外,大对象(新生代 eden 区无法装入时,也会直接进入老年代)。JVM 里面有个参数(PretenureSizeThreshold)可以设置对象的大小超过在指定的大小之后,直接晋升老年代。

7. TLAB

  • TLAB 全称是 Thread Local Allocation Buffer 即线程本地分配缓存。从名字上看是一个线程专用的内存分配缓存,从名字上看是一个线程专用的内存分配区域,是为了加速对象分配而生的。每一个线程都会产生一个 TLAB,该线程独享的工作区域,Java 虚拟机使用这种 TLAB 区来避免多线程冲突问题,提高了对象分配的效率。TLAB 空间一个一般不会太大,当大对象无法再 TLAB 分配时,则会直接分配到堆上。

  • -XX:+UseTLAB:使用 TLAB

  • -XX:+TLABSize:设置 TLAB 大小

  • -XX:TLABRefillWasterFraction:设置维护进入 TLAB 空间的单个对象大小,他是一个比例值,默认为 64,即如果对象大于整个空间的 1/64,则在堆创建对象

  • -XX:+PrintTLAB:查看 TLAB 信息

  • -XX:ResizeTLAB:自调整 TLABRefillWasterFraction 阈值

    public class Test06 {
    
        public static void main(String[] args) {
    
            //测试进入老年代的对象
            //
            //参数:-Xmx30M -Xms30M -XX:+UseSerialGC -XX:+PrintGCDetails -XX:PretenureSizeThreshold=10240000
            //-XX:+PrintHeapAtGC
    		/*Map<Integer, byte[]> m1 = new HashMap<Integer, byte[]>();
    		for(int i =0; i <5 ; i++) {
    			byte[] b = new byte[1024*1024];
    			m1.put(i, b);
    		}*/
    
            //参数:-Xmx30M -Xms30M -XX:+UseSerialGC -XX:+PrintGCDetails -XX:PretenureSizeThreshold=1000
            //这种现象原因为:虚拟机对于体积不大的对象 会优先把数据分配到TLAB区域中,因此就失去了在老年代分配的机会
            //参数:-Xmx30M -Xms30M -XX:+UseSerialGC -XX:+PrintGCDetails -XX:PretenureSizeThreshold=1000 -XX:-UseTLAB
            Map<Integer, byte[]> m2 = new HashMap<Integer, byte[]>();
            for (int i = 0; i < 5 * 1024; i++) {
                byte[] b = new byte[1024];
                m2.put(i, b);
            }
        }
    
    }

猜你喜欢

转载自blog.csdn.net/masteryourself/article/details/84753054
03.