JVM---GC-续
分代收集算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /** * 【GC---分代收集算法】 * ***没有最好的算法,只有最合适的算法; * * 基于 不同对象的生命周期不一样,因此,不同生命周期的对象可以采取不同的收集方式,以提高回收效率; * * 目前几乎所有的 垃圾收集器 都采用 分代收集算法 ; * eg:Hotspot中: * 年轻代: * 特点: * 对象朝生夕死,存活率低,回收频繁; * 使用 复制算法 Copying; * * 老年代: * 特点: * 对象生命周期长,回收不频繁; * 使用 标记-清除Mark-Sweep 或 标记-压缩Mark-Compact 算法; * 标记的开销 与 可达对象的数量 成正比; * 清除的开销 与 内存区域大小 成正比; * 压缩的开销 与 可达对象数量 成正比; */ |
增量收集算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /** * 【GC---增量收集算法】 * 解决什么问题? * Mark-Sweep、Copying、Mark-Compact算法,在垃圾回收过程中,会进行STW,用户线程都被挂起; * 如果垃圾回收时间过长,将严重影响用户体验; * 为了解决这个问题,增量收集算法 诞生; * * 思想: * 让 垃圾收集线程 与 用户线程 交替进行: * 每次垃圾收集线程 只收集一小片区域的内存空间,接着切换到用户线程; * 依次反复; * * 本质: * 增量收集算法的基础 仍是 标记-清除、复制算法; * 通过对线程间的妥善处理,允许垃圾收集线程以分阶段的方式完成标记-清除工作; * * 缺点: * 因为线程切换和上下文切换的消耗,会使得垃圾回收的总体成本上升; */ |
分区算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /** * 【GC---分区算法】 * 解决什么问题? * 在相同条件下,堆空间越大,一次GC所需要的时间越长; * 为了更好控制GC时间,可以将一块大的区域分割成多个小块,每次合理的回收若干小块; * * 分区算法 与 分代算法的区别 * 分代算法: * 按对象的生命周期,划分为新生代、老年代; * 分区算法: * 将整个堆空间划分为连续不同的小区间region; * 每个小区间region独立使用,独立回收; * */ |
分区算法
栈-栈桢-局部变量表GC测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | 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(); } } |
内存泄漏
后几个对象本来不被使用了,但是还有一个引用,导致无法被回收;
垃圾回收相关概念
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | /** * 【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时,收到一个系统通知; */ |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | 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 + '\ '' + '}' ; } } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
2019-05-09 环境命令指南
2019-05-09 使用指南