学习权重30%。 理解问题: 垃圾收集器的特点与 运作原理,JVM自动内存分配与回收的主要规则。 目录
判定对象存活
引用计数法Reference Counting
缺陷
可达性分析(Reachability Analysis)算法
可作为GC Roots的对象
引用的扩展
举行对象回收
回收方法区
垃圾收集算法
分代收集理论
收集分类
部分收集(Partial GC)
整堆收集(Full GC)
标记-清除算法(mark-sweep)
缺点
标记-复制算法
优点
缺点
标记-整理算法 Mark-Compact
根节点枚举
记忆集 Remembered Set(跨代对象引用集合)
卡表的维护
并发可达性分析
收集器
收集器的差异与选择
生产
Full GC监控与问题排查
判定对象存活
引用计数法Reference Counting
在对象中添加一个计数器,有引用就+1,引用失效就-1。 缺陷
无法解决对象相互引用导致无法回收的问题。 可达性分析(Reachability Analysis)算法
可达性分析算法理论上要责备过程都基于一个能保障一致性的快照中才可以或许举行分析, 这意味着必须全程冻结用户线程的运行。 JVM利用增量更新和原始快照两种方案来实现可达性分析的并发,用于减少可达性分析的停顿时间。 以根对象GC Roots为起点,依据引用向下搜刮,搜刮过程所走过的路径称为“引用链”(Reference Chain),如果对象到GC Roots间没有任何引用链相连,则对象不可达。 可作为GC Roots的对象
在Java技术体系里面,固定可作为GC Roots的对象包罗以下几种:
- 在虚拟机栈(栈帧中的当地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中利用到的 参数、局部变量、暂时变量等。
- 在方法区中类静态属性引用的对象,譬如Java类的引用范例静态变量。
- 在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用。
- 在当地方法栈中JNI(即通常所说的Native方法)引用的对象。
- Java虚拟机内部的引用,如基本数据范例对应的Class对象,一些常驻的异常对象(比如 NullPointExcepiton、OutOfMemoryError)等,另有系统类加载器。
- 全部被同步锁(synchronized关键字)持有的对象。
- 反映Java虚拟机内部环境的JMXBean、JVMTI中注册的回调、当地代码缓存等。
引用的扩展
Java希望对象不是只有“引用”、“未被引用”两种状态,有些对象现可以判定空间是否富足再举行回收。因此,拓展了引用的概念。 弱引用、强引用、软引用、虚引用 强:为null才会在合适时间被清除(JVM停止运行时)。 软:内存不敷时JVM才会回收 弱:岂论内存富足,都会回收,生命周期更短。 虚:任何时间都可以回收。 引用范例 | 被回收时间 | 阐明 | 强引用 | 无引用关系时 | 传统引用定义,引用赋值 Object o = new Object()这种 | 软引用 | 内存溢出异常前 | 对象缓存 | 弱引用 | 下一次垃圾收集前 | 对象缓存 | 虚引用 | 随关联对象 | 为一个对象设置虚 引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知 | 举行对象回收
对象经历一次标记、一次筛选、一次标记后被实行回收。 第一次可达性分析判定不可达举行标记,第二次筛选是否需要实行finalize()方法。将需要实行finalize()方法的对象防至F-Queue队列,然后标记完成。 注:避免利用finalize()方法 回收方法区
可以通过参数设置。 方法区垃圾收集的性价比很低。但在频繁自定义类加载器的场景中,还是需要java虚拟机具备类卸载的能力。 垃圾收集算法
JVM利用的是“Tarcing GC 追踪式垃圾收集”范例的算法 分代收集理论
1、弱分代假说(Weak Generational Hypothesis):绝大多数对象都是朝生夕灭的。 2、强分代假说(Strong Generational Hypothesis):熬过越多次垃圾收集过程的对象就越难以消 亡。 3、跨代引用假说(Intergenerational Reference Hypothesis):跨代引用相对于同代引用来说仅占极 少数。 按照对象熬过垃圾收集过程的次数举行分代,即区域分别。 不同区域实行垃圾收集回收的频次不一样、垃圾收集算法也不一样。 对于跨代引用,利用特殊内存区域存储跨代引用对象。 收集分类
部分收集(Partial GC)
指目的不是完整收集整个Java堆的垃圾收集,此中又分为:
目的只是新生代的垃圾收集,Eden区满触发。
指目的只是老年代的垃圾收集。目前只有CMS收集器会有单 独收集老年代的行为。别的请注意“Major GC”这个说法现在有点混淆,在不同资料上常有不同所指, 读者需按上下文区分到底是指老年代的收集还是整堆收集。 停顿时间较长,但比Full GC短。 老年代的空间利用率到达一定阈值时触发,通过 -XX:CMSInitiatingOccupancyFraction参数设置老年代占用比例。默认值
指目的是收集整个新生代以及部分老年代的垃圾收集。目前只有G1收 集器会有这种行为。 整堆收集(Full GC)
收集整个Java堆和方法区的垃圾收集。一种重型的垃圾回收操作,通常导致程序线程暂停(Stop-The-World STW)。 可以通过System.gc()方法调用举行FullGC。 或是在老年代空间不敷、堆内存空间不敷时举行Full GC。 可以开启GC日志举行分析。 标记-清除算法(mark-sweep)
最早出现的基础算法。 首先标记出全部需要回 收的对象,在标记完成后,统一回收掉全部被标记的对象,也可以反过来,标记存活的对象,统一回 收全部未被标记的对象。 标记的过程就是可达性分析的过程。 需要停顿用户线程来举行标记、清理回收对象,不外停顿时间相对要短。 缺点
需要大量标记、清除操作,效率低; 内存空间碎片化问题严肃,碎片化会导致连续空间不敷不好存储对象从而触发再一次的垃圾收集工作。 标记-复制算法
目的是解决“标记-清除算法”回收大量对象效率低的问题。 将可用 内存按容量分别为大小相称的两块,每次只利用此中的一块。当这一块的内存用完了,就将还存活着 的对象复制到别的一块上面,然后再把已利用过的内存空间一次清理掉。 根据分署理论,需要复制的活着的对象是少数的。所以每次复制的对象不多。 优点
避免碎片产生,高效 缺点
可用内存为原来的一半,空间浪费严肃 标记-整理算法 Mark-Compact
为对象存活率高的区域设计的算法,针对老年代对象。 让全部存活的对象都向内存空间的一端移动,直接清理掉边界以外的内存。 注意:移动对象需要更改对象全部引用,需要暂停全部用户程序才气举行。总的来说,JVM设计者做了内存分配、内存回收的权衡以提拔总吞吐量。 吞吐量=运行用户代码时间/(运行用户代码时间+运行垃圾收集时间) 根节点枚举
可达性分析时,需要先找到根节点GC Roots然后再查找引用链。 HotSpot利用OopMap( Ordinary Object Pointer Map对象指针Map)数据结构存储根节点对象引用。 在OopMap帮忙下HotSpot可以快速准确完成GC Roots枚举。 记忆集 Remembered Set(跨代对象引用集合)
依据分代收集理论的跨代假说,跨代对象极少,而垃圾收集器针对跨代对象的特殊数据结构就是记忆集Remembered Set。用以避免把整个老年代加进GC Roots扫描范围、或是跨代引起的范围扫描。 记忆集是一种 用于记录从非收集区域指向收集区域的指针集合(跨代引用集合)的抽象数据结构。 集合对象的粒度可以分为:
- 字长精度:每个记录精确到一个机器字长(就是处理器的寻址位数,如常见的32位或64位,这个 精度决定了机器访问物理内存地址的指针长度),该字包罗跨代指针。
- 对象精度:每个记录精确到一个对象,该对象里有字段含有跨代指针。
- 卡精度:每个记录精确到一块内存区域,该区域内有对象含有跨代指针。
最常用的是“卡精度”,Card Table卡表就是卡精度下记忆集的一种实现。 总结:对于如何避免跨代引用问题造成的大范围扫描本质还是通过标识来实现的。标识出这一块内存有跨代指针,然后就可以将这一块内存加入GC Root中举行扫描。有点雷同索引避免全表扫描。 卡表的维护
卡表标识的时间:有其他分代区域中对象引用了本区域对象时,其对应的 卡表元素就应该变脏(举行标识)。 如何在对象赋值的那一刻去更新维护卡表呢:须找到一个在机器码层面的本事,把维护卡表的动作放到每一 个赋值操作之中。 总结:操作附带同步变化,相干性维护。 并发可达性分析
已知标记对象举行可达性分析时需要暂停用户进程。 JVM利用增量更新和原始快照两种方案来实现可达性分析的并发,用于减少可达性分析的停顿时间。 详细实现阐明略。 收集器
Serial收集器:针对新生代,采用复制算法。 ParNew(并行)收集器:新生代采用复制算法,可与CMS收集器配合利用于老年代。 Parallel Scavenge(并行)收集器 (JDK8默认新生代):针对新生代,采用复制算法,注意吞吐量。 Serial Old(串行)收集器:针对老年代,采用标记-整理算法。 Parallel Old(并行)收集器 (JDK8默认老年代):针对老年代,采用标记-整理算法,是Parallel Scavenge的老年代版本。 CMS(Concurrent Mark-Sweep)收集器:主要作用于老年代,基于标记-清除算法,但为了减少内存碎片,有时也会举行整理。 G1(Garbage First)收集器:整体上采用标记-整理算法,局部(如年轻代)采用复制算法。 综上:新生代基本采用“标记-复制算法”,老年代采用“标记-整理算法”。cms采用标记清理。 收集器的差异与选择
JDK8默认收集器为新生代Parallel Scavenge和Parallel Old,JKD9开始G1收集器成为默认收集器雏形。 G1相较于默认的两个收集器,通过分区域管理和预测停顿时间,有更低更可控的耽误时间。 G1低耽误原理:分别更多的独立区域,优先回收垃圾最多的区域。支持并发标记和部分整理。 并发和分区管理带来的开销更高。 维度 | Parallel Scavenge + Parallel Old | G1 | 吞吐量 | 高,适合批处理使命 | 稍低,但仍可担当 | 停顿时间 | 较长,随堆增大明显增加 | 可控,支持低耽误目的 | 堆大小适应性 | 小堆表现精良,大堆停顿变长 | 专为大堆设计(>6GB 更优) | 内存碎片 | 少,整理算法有用 | 较少,但极端环境可能触发 Full GC | 调优复杂度 | 低,几乎无需调整 | 中等,需要根据应用调整参数 | 适用场景 | 计算密集型、耽误不敏感 | 交互式应用、大内存、耽误敏感 | 总结:可用内存>6G。追求地耽误就选G1 生产
Full GC监控与问题排查
- 可以利用jstat -gc命令举行监控
- 可以通过参数-XX:+PrintGCDetails -Xloggc: 开启GC日志
- 可以通过jmap生成堆转储文件来看
日志分析 → 工具监控 → 原因定位 → 参数/代码优化 → 验证
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |