JVM---GC-续

 

分代收集算法

/**
     *  【GC---分代收集算法】
     *      ***没有最好的算法,只有最合适的算法;
     *
     *      基于 不同对象的生命周期不一样,因此,不同生命周期的对象可以采取不同的收集方式,以提高回收效率;
     *
     *      目前几乎所有的 垃圾收集器 都采用 分代收集算法 ;
     *          eg:Hotspot中:
     *              年轻代:
     *                  特点:
     *                      对象朝生夕死,存活率低,回收频繁;
     *                  使用 复制算法 Copying;
     *
     *              老年代:
     *                  特点:
     *                      对象生命周期长,回收不频繁;
     *                  使用 标记-清除Mark-Sweep 或 标记-压缩Mark-Compact 算法;
     *                      标记的开销 与 可达对象的数量 成正比;
     *                      清除的开销 与 内存区域大小 成正比;
     *                      压缩的开销 与 可达对象数量 成正比;
     */

 

增量收集算法 

/**
     *  【GC---增量收集算法】
     *      解决什么问题?
     *          Mark-Sweep、Copying、Mark-Compact算法,在垃圾回收过程中,会进行STW,用户线程都被挂起;
     *          如果垃圾回收时间过长,将严重影响用户体验;
     *          为了解决这个问题,增量收集算法 诞生;
     *
     *      思想:
     *          让 垃圾收集线程 与 用户线程 交替进行:
     *              每次垃圾收集线程 只收集一小片区域的内存空间,接着切换到用户线程;
     *              依次反复;
     *
     *      本质:
     *          增量收集算法的基础 仍是 标记-清除、复制算法;
     *          通过对线程间的妥善处理,允许垃圾收集线程以分阶段的方式完成标记-清除工作;
     *
     *      缺点:
     *          因为线程切换和上下文切换的消耗,会使得垃圾回收的总体成本上升;
     */

  

分区算法

/**
     *  【GC---分区算法】
     *      解决什么问题?
     *          在相同条件下,堆空间越大,一次GC所需要的时间越长;
     *          为了更好控制GC时间,可以将一块大的区域分割成多个小块,每次合理的回收若干小块;
     *
     *      分区算法 与 分代算法的区别
     *          分代算法:
     *              按对象的生命周期,划分为新生代、老年代;
     *          分区算法:
     *              将整个堆空间划分为连续不同的小区间region;
     *              每个小区间region独立使用,独立回收;
     *
     */

  

分区算法

 

栈-栈桢-局部变量表GC测试

public class LocalVarGCTest {

    static void test1(){
        byte[] bytes = new byte[10*1024*1024];
        System.gc();

        /**
         * 未回收
         * [GC (System.gc()) [PSYoungGen: 14176K->10736K(76288K)] 14176K->10808K(251392K), 0.0088785 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
         * [Full GC (System.gc()) [PSYoungGen: 10736K->0K(76288K)] [ParOldGen: 72K->10681K(175104K)] 10808K->10681K(251392K), [Metaspace: 3287K->3287K(1056768K)], 0.0128084 secs] [Times: user=0.01 sys=0.02, real=0.01 secs]
         * Heap
         *  PSYoungGen      total 76288K, used 655K [0x000000076ab00000, 0x0000000770000000, 0x00000007c0000000)
         *   eden space 65536K, 1% used [0x000000076ab00000,0x000000076aba3ee8,0x000000076eb00000)
         *   from space 10752K, 0% used [0x000000076eb00000,0x000000076eb00000,0x000000076f580000)
         *   to   space 10752K, 0% used [0x000000076f580000,0x000000076f580000,0x0000000770000000)
         *  ParOldGen       total 175104K, used 10681K [0x00000006c0000000, 0x00000006cab00000, 0x000000076ab00000)
         *   object space 175104K, 6% used [0x00000006c0000000,0x00000006c0a6e5d8,0x00000006cab00000)
         *  Metaspace       used 3293K, capacity 4496K, committed 4864K, reserved 1056768K
         *   class space    used 363K, capacity 388K, committed 512K, reserved 1048576K
         */
    }

    static void test2(){
        byte[] bytes = new byte[10*1024*1024];
        bytes = null;
        System.gc();

        /**
         * 已回收
         * [GC (System.gc()) [PSYoungGen: 14176K->608K(76288K)] 14176K->616K(251392K), 0.0009482 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
         * [Full GC (System.gc()) [PSYoungGen: 608K->0K(76288K)] [ParOldGen: 8K->414K(175104K)] 616K->414K(251392K), [Metaspace: 3243K->3243K(1056768K)], 0.0043901 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
         * Heap
         *  PSYoungGen      total 76288K, used 655K [0x000000076ab00000, 0x0000000770000000, 0x00000007c0000000)
         *   eden space 65536K, 1% used [0x000000076ab00000,0x000000076aba3ee8,0x000000076eb00000)
         *   from space 10752K, 0% used [0x000000076eb00000,0x000000076eb00000,0x000000076f580000)
         *   to   space 10752K, 0% used [0x000000076f580000,0x000000076f580000,0x0000000770000000)
         *  ParOldGen       total 175104K, used 414K [0x00000006c0000000, 0x00000006cab00000, 0x000000076ab00000)
         *   object space 175104K, 0% used [0x00000006c0000000,0x00000006c0067a20,0x00000006cab00000)
         *  Metaspace       used 3250K, capacity 4496K, committed 4864K, reserved 1056768K
         *   class space    used 357K, capacity 388K, committed 512K, reserved 1048576K
         */
    }

    static void test3(){
        {
            byte[] bytes = new byte[10*1024*1024];
        }

        System.gc();

        /**
         * 未回收
         * [GC (System.gc()) [PSYoungGen: 14176K->10720K(76288K)] 14176K->10772K(251392K), 0.0078192 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
         * [Full GC (System.gc()) [PSYoungGen: 10720K->0K(76288K)] [ParOldGen: 52K->10681K(175104K)] 10772K->10681K(251392K), [Metaspace: 3287K->3287K(1056768K)], 0.0091166 secs] [Times: user=0.01 sys=0.02, real=0.01 secs]
         * Heap
         *  PSYoungGen      total 76288K, used 655K [0x000000076ab00000, 0x0000000770000000, 0x00000007c0000000)
         *   eden space 65536K, 1% used [0x000000076ab00000,0x000000076aba3ee8,0x000000076eb00000)
         *   from space 10752K, 0% used [0x000000076eb00000,0x000000076eb00000,0x000000076f580000)
         *   to   space 10752K, 0% used [0x000000076f580000,0x000000076f580000,0x0000000770000000)
         *  ParOldGen       total 175104K, used 10681K [0x00000006c0000000, 0x00000006cab00000, 0x000000076ab00000)
         *   object space 175104K, 6% used [0x00000006c0000000,0x00000006c0a6e5d8,0x00000006cab00000)
         *  Metaspace       used 3294K, capacity 4496K, committed 4864K, reserved 1056768K
         *   class space    used 363K, capacity 388K, committed 512K, reserved 1048576K
         */
    }

    static void test4(){
        {
            byte[] bytes = new byte[10*1024*1024];
        }

        int a = 0; // 局部变量表的slotsSize=1 ,代码块中的bytes 与 a 共用一个slot,a覆盖掉bytes,导致 bytes指向的对象为垃圾对象
        System.gc();

        /**
         * 已回收
         * [GC (System.gc()) [PSYoungGen: 14176K->608K(76288K)] 14176K->616K(251392K), 0.0010316 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
         * [Full GC (System.gc()) [PSYoungGen: 608K->0K(76288K)] [ParOldGen: 8K->414K(175104K)] 616K->414K(251392K), [Metaspace: 3243K->3243K(1056768K)], 0.0046198 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
         * Heap
         *  PSYoungGen      total 76288K, used 1966K [0x000000076ab00000, 0x0000000770000000, 0x00000007c0000000)
         *   eden space 65536K, 3% used [0x000000076ab00000,0x000000076aceb9e0,0x000000076eb00000)
         *   from space 10752K, 0% used [0x000000076eb00000,0x000000076eb00000,0x000000076f580000)
         *   to   space 10752K, 0% used [0x000000076f580000,0x000000076f580000,0x0000000770000000)
         *  ParOldGen       total 175104K, used 414K [0x00000006c0000000, 0x00000006cab00000, 0x000000076ab00000)
         *   object space 175104K, 0% used [0x00000006c0000000,0x00000006c00679b8,0x00000006cab00000)
         *  Metaspace       used 3250K, capacity 4496K, committed 4864K, reserved 1056768K
         *   class space    used 357K, capacity 388K, committed 512K, reserved 1048576K
         */
    }

    static void test5(){
        test1();

        System.gc();

        /**
         * test1中未回收
         * test5中已回收
         * [GC (System.gc()) [PSYoungGen: 14176K->10720K(76288K)] 14176K->10784K(251392K), 0.0130876 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
         * [Full GC (System.gc()) [PSYoungGen: 10720K->0K(76288K)] [ParOldGen: 64K->10654K(175104K)] 10784K->10654K(251392K), [Metaspace: 3243K->3243K(1056768K)], 0.0163820 secs] [Times: user=0.01 sys=0.02, real=0.01 secs]
         * [GC (System.gc()) [PSYoungGen: 0K->0K(76288K)] 10654K->10654K(251392K), 0.0003257 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
         * [Full GC (System.gc()) [PSYoungGen: 0K->0K(76288K)] [ParOldGen: 10654K->414K(175104K)] 10654K->414K(251392K), [Metaspace: 3243K->3243K(1056768K)], 0.0054800 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
         * Heap
         *  PSYoungGen      total 76288K, used 1966K [0x000000076ab00000, 0x0000000770000000, 0x00000007c0000000)
         *   eden space 65536K, 3% used [0x000000076ab00000,0x000000076aceb9e0,0x000000076eb00000)
         *   from space 10752K, 0% used [0x000000076f580000,0x000000076f580000,0x0000000770000000)
         *   to   space 10752K, 0% used [0x000000076eb00000,0x000000076eb00000,0x000000076f580000)
         *  ParOldGen       total 175104K, used 414K [0x00000006c0000000, 0x00000006cab00000, 0x000000076ab00000)
         *   object space 175104K, 0% used [0x00000006c0000000,0x00000006c00679b8,0x00000006cab00000)
         *  Metaspace       used 3268K, capacity 4496K, committed 4864K, reserved 1056768K
         *   class space    used 359K, capacity 388K, committed 512K, reserved 1048576K
         */
    }

    public static void main(String[] args) {
        LocalVarGCTest.test5();
    }


}

 

内存泄漏

 

后几个对象本来不被使用了,但是还有一个引用,导致无法被回收

 

垃圾回收相关概念

/**
     *  【GC---垃圾回收相关概念】
     *      <System.gc()或Runtime.getRuntime().gc()>
     *          默认情况下,通过System.gc()或Runtime.getRuntime().gc()的调用,显式触发Full GC,同时对新生代和老年代进行回收;
     *
     *          System.gc()或Runtime.getRuntime().gc() 无法保证对 垃圾收集器 的调用;
     *
     *          System.runFinalization() 强制 垃圾收集器 调用垃圾对象的finalize();
     *
     *      <内存溢出OOM>
     *          没有空闲内存空间 且 GC后也无法提供更多内存空间;
     *
     *          原因:
     *              1、JVM的堆内存设置不够
     *                  可能存在内存泄漏问题;
     *                  可能是堆大小不合适;
     *              2、创建大量大对象, 且 长时间被引用;
     *
     *          一般情况下,在抛出OOM之前,通常 垃圾收集器 被触发 进行GC;
     *          特殊情况下,直接抛出OOM
     *              eg:分配一个超大对象,JVM判断出垃圾收集器不能解决这个问题,直接抛出OOM;
     *
     *      <内存泄漏Memory Leak>
     *          what
     *              严格意义:
     *                  只有对象不再被程序使用,但GC又不能回收;
     *
     *              宽泛意义:
     *                  一些不太好的使用 导致对象的生命周期变得很长甚至导致OOM;
     *
     *          场景
     *              1、单例模式
     *                  单例对象的生命周期和应用程序一样长,如果单例对象引用其他对象,会导致外部对象不被回收,产生内存泄漏;
     *
     *              2、资源未关闭导致内存泄漏
     *                  数据库连接、网络连接、IO连接 必须手动close,否则不能被回收;
     *
     *      <STW>
     *          what
     *              Stop The World,在GC发生过程中,导致用户线程暂停;
     *
     *              所有的垃圾回收器 都有STW事件;
     *
     *              不要显式调用System.gc(),会造成STW;
     *
     *      <垃圾回收的并行与并发>
     *          并行Parallel:
     *              多个 垃圾收集线程 并行工作,用户线程被暂停;
     *              eg:ParNew、Parallel Scavenge、Parallel Old;
     *
     *          串行Serial:
     *              单个 垃圾收集线程 执行;
     *
     *          并发Concurrent:
     *              用户线程 与 垃圾收集线程 同时执行,垃圾收集线程不会暂停用户线程;
     *              eg:CMS、G1
     *
     *      <安全点Safepoint与安全区域Safe Region>
     *          安全点
     *              what
     *                  程序执行时,并非在所有地方都能停顿下来开始GC,只有在特定的位置才能停顿下来,这些位置叫 安全点Safepoint;
     *
     *              安全点的选择很重要:
     *                  如果太少,可能导致GC等待时间太长、如果太频繁,可能导致运行时性能;
     *                  大部分指令的执行时间都非常短暂,通常会根据 是否具有让程序长时间执行的特征 为标准,比如选择一些执行时间较长的指令为安全点:
     *                      eg:方法调用、循环跳转、异常跳转...
     *
     *              如何在GC发生时,检查所有线程都运行到最近的安全点停顿下来?
     *                  抢先式中断(目前没有JVM采用):
     *                      首先中断所有线程,如果有线程不在安全点,恢复线程,让线程运行至安全点;
     *
     *                  主动式中断:
     *                      设置一个中断标志,各个线程运行到安全点时主动轮询这个标志,如果中断标志为true,将自己线程中断挂起;
     *
     *          安全区域
     *
     *              ???
     *
     *      <Java中的引用>
     *          需求:
     *              希望描述一类对象,当内存空间足够时,保留在内存中;如果内存空间紧张 且 GC后还是紧张,可以抛弃;
     *
     *          解决:
     *              JDK1.2之后,Java对引用概念进行了扩展,分为 强引用Strong Reference、软引用Soft Reference、弱引用Weak Reference、虚引用Phantom Reference,引用强度依次减弱;
     *              在java.lang.ref包中可以找到java.lang.ref.SoftReference、java.lang.ref.WeakReference、java.lang.ref.PhantomReference
     *
     *              <强引用>
     *                  what
     *                      传统引用的定义即引用赋值,如 Object o = new Object();
     *                      强引用关系,无论任何情况下,只要强引用关系存在,垃圾收集器 不会回收 被强引用的对象;
     *                  特点:
     *                      强引用可以直接访问目标对象;
     *                      强引用对象都是可达的;
     *                      对于一个强引用对象,如果没有任何引用关系(超过引用关系作用域或显式赋值为null),就被当做垃圾对象;
     *
     *                  ***强引用也是造成Java内存泄漏的主要原因之一;
     *
     *              <软引用>
     *                  what
     *                      在系统将发生OOM之前,会把这些软引用对象列入回收范围进行二次回收,如果回收后还没足够的内存空间,才会抛出OOM;
     *
     *                  JDK实现:
     *                      java.lang.ref.SoftReference
     *
     *                  场景:
     *                      缓存
     *
     *              <弱引用>
     *                  what
     *                      只要GC,无论内存空间是否足够,都会被回收;
     *
     *                  JDK实现:
     *                      java.lang.ref.WeakReference
     *
     *                  eg:
     *                      WeakHashMap
     *
     *                      java.util.WeakHashMap{
     *                          Entry<K,V>[] table;
     *
     *                          private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {}
     *                      }
     *
     *              <虚引用>
     *                  what
     *                      一个对象是否有虚引用存在,完全不会对该对象的生命周期产生影响;
     *                      无法通过虚引用获得一个对象实例;
     *
     *                  目的
     *                      能在这个对象被 垃圾收集器GC时,收到一个系统通知;
     */

  

public class ReferenceTest {

    public static void main(String[] args) {
//        softTest();
        weakTest();
    }

    static void weakTest(){
        // 建立弱引用
        User user = new User(1, "rose");
        WeakReference<User> weakReference = new WeakReference<User>(user);

        // 移除强引用
        user = null;

        // 弱引用获取对象
        System.out.println(weakReference.get());

        System.gc();
        System.out.println("after gc...");

        System.out.println(weakReference.get()); // 一旦GC,就回收弱引用对象

        /**
         * 结果:
         * User{age=1, name='rose'}
         * after gc...
         * null
         */
    }

    static void softTest(){
        // -Xms10m -Xmx10m

        // 建立软引用
        User user = new User(1, "rose");
        SoftReference<User> softReference = new SoftReference<User>(user);

        // 移除强引用关系
        user = null;

        // 软引用获取对象
        System.out.println(softReference.get());

        System.gc();
        System.out.println("after gc...");

        System.out.println(softReference.get()); // 由于内存空间足,不会回收软引用对象

        try {
            byte[] bytes = new byte[1024*1024*7];
        }catch (Throwable e){
            e.printStackTrace();
        }finally {
            System.out.println(softReference.get()); // 由于内存空间不足,回收软引用对象
        }

        /**
         * 结果:
         * User{age=1, name='rose'}
         * after gc...
         * User{age=1, name='rose'}
         * java.lang.OutOfMemoryError: Java heap space
         * 	at com.an.gc.ReferenceTest.softTest(ReferenceTest.java:34)
         * 	at com.an.gc.ReferenceTest.main(ReferenceTest.java:14)
         * null
         */

    }

    private static class User{
        private int age;
        private String name;

        public User(int age, String name){
            this.age = age;
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        @Override
        public String toString() {
            return "User{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
}

  

 

 

posted on 2022-05-09 16:58  anpeiyong  阅读(29)  评论(0)    收藏  举报

导航