jvm笔记-02-垃圾收集器与内存分配策略

[TOC]

对象的生存与死亡

如何判定一个对象的生与死

  1. 引用计数法 Reference Counting

    给对象中添加一个引用计数器,被引用加1,引用失效减1,计数器为0则对象可以被回收。

    缺点:很难解决对象之间互相循环引用的问题。

  2. 可达性分析法 Reachability Analysis

    通过一系列 GC Roots 的对象作为起点,向下搜索,遍历路径称为引用链。当一个对象到 GC Roots 不可达时,证明此对象是不可用的。

    GC Roots对象包括:

    1. 虚拟机栈中引用的对象
    2. 方法区中静态常量引用对象
    3. 方法区中常量引用对象
    4. 本地方法栈中引用的对象

死亡面前,各种引用不平等

这样的一类对象:当内存空间足够是,保存在内存之中。若内存空间在垃圾回收之后仍不足,则可以抛弃这些对象。应用场景如系统的缓存功能。

  1. 强引用

    普遍存在的引用,只要引用关系存在,就不会被回收。

  2. 软引用

    还有用但非必须的对象,系统将要发生内存溢出之前,将会把这些对象列入回收范围之内。 SoftReference 类实现。

  3. 弱引用

    描述非必须对象,比软引用更弱一些,被弱引用关联对象只能生存到下一次垃圾收集之前。 WeakReference 类实现。

  4. 虚引用

    幽灵引用或幻影引用,不会对对象生存时间造成影响,也无法通过虚引用获取对象实例。

    为对象设置虚引用关联唯一目的就是能在这个对象被回收时收到一个系统通知。 PhantomReference 类实现。

死亡,不过如此 finalize()方法

可达性分析算法中不可达的对象,到被回收之间至少经历两次标记过程:

  1. 对象在进行可达性分析之后发现没有与 GC Roots的引用链,会被第一次标记并筛选。

    筛选的条件是对象是否有必要去执行 finalize() 方法。

    当对象没有覆盖该方法,或该方法已经被虚拟机调用过,则没必要执行。

  2. 有必要执行 finalize() 方法的对象,被放入 F-Queue 队列中触发该方法。

    这是对象自我拯救的最后一次机会:将自己赋值给某个类变量或成员变量。

    稍后 GC 将对 F-Queue 中对象进行第二次标记,正式宣告对象死亡。

有点像寻梦环游记里的,如果活着的人没人记得你,你就会在亡灵世界里终极死亡。

回收方法区(永久代)

永久代垃圾回收主要是两部分内容:废弃常量与无用的类。

判断常量废弃的条件:

没有任何对象引用常量池中的常量,如字面量、接口、方法、字段的符号引用,垃圾回收时必要时会被回收。

判断类无用的条件:

1. 该类的所有实例已被回收
2. 加载该类的 ClassLoader 已被回收
3. 该类对应的 java.lang.Class对象没有被引用,即无法通过反射访问该类方法。

回收无用的类型,使用 -Xnoclassgc 控制。

在大量使用反射、动态代理、CGLib等字节码框架、动态生成JSP以及OSGI频繁自定义ClassLoader的场景,需要虚拟机具备类卸载功能。

垃圾收集算法

标记-清除算法 Mark-Sweep

原理:

  1. 标记阶段:对于不可达的对象进行标记。
  2. 清除阶段:对两次标记的对象进行回收。

缺点:

  1. 效率问题:标记和清除的效率都不高
  2. 空间问题:会产生大亮不连续的空间碎片,须分配较大对象时,会因为无法找到足够大的内存而提前触发垃圾收集动作。

复制算法 Copying

原理:

将内存划分为两块,每次使用其中一块,使用完时将其中存活对象复制到另一块,将已经使用的内测空间清理掉。

优缺点:

实现简单,运行高效。

可用内存空间变小,且只适用于年轻代。

内存分配:

新生代分为较大的 Eden 空间和两块较小的 Survivor 空间。每次使用 Eden 和 其中一块 Survivor空间。

HotSpot虚拟机默认 EdenSurvivor = 8:1

当回收时 Survivor 空间不足,需要老年代进行分配担保。

标记整理算法 Mark-Compact

原理:

  1. 标记同 标记-清除算法 中的标记阶段。
  2. 整理:让所有存活对象向一端移动,然后清理掉边界以外的内存。

优点:

  1. 解决 复制算法 在对象存活率较高时复制操作效率低问题。适用于老年代。
  2. 不会浪费内存空间。

分代收集算法 Generational Collection

根据对象存活周期不同将内存划分为新生代和老年代。

新生代对象存活少,选用 复制算法

老年代对象存活率高、没有额外空间分配担保,选用 标记-清除算法标记整理算法

hotspot的算法实现

枚举根节点 GC Roots

原理:

作为GC Roots的节点主要在全局性的引用(常量和静态属性)和执行上下文(栈帧的本地变量表)中。

难点:

  1. 方法区可能较大,逐个检查引用效率低。
  2. GC停顿,整个执行系统中引用关系不断变化会影响分析结果准确性。Stop The Word

解决:

  1. 使用一组称为 OopMap 的数据结构,直接得知执行上下文和全局引用的位置。
  2. 使用 OopMap 可快速完成 GC Roots枚举,但前提是枚举时引用关系不会变化。这个使用安全点 Safepoint 解决。

安全点 Safepoint

问题:

GC Roots枚举时可能引用关系变化,或者说,导致 OopMap 内容变化的指令非常多,若为每条指令生成 OopMap,将会需要大量额外空间。

解决:

只在特定位置记录 OopMap 信息,这些位置即安全点。

程序执行到安全点才可以GC。

安全点的选定:

是以程序是否具有让程序长时间执行的特征为标准选定的,特征是指令序列复用,如方法调用、循环跳转、异常跳转等。

意思是在你经常路过的地方堵着你。

如何在GC发生时让所有线程都泡到安全点上停顿下来?

  1. 抢先式中断

    先把所有线程全部中断,如果有线程中断的地方不在安全点上,就恢复线程让它跑到安全点上。

  2. 主动式中断

    当GC需要中断线程时,见设置一个标识,个线程执行时主动轮询该标识,发现为真时就自己中断挂起。轮询标志的地方与安全点重合。

安全区域 SafeRegion

Safepoint 的问题:

若程序不执行如处于 SleepBlocked 状态的线程,无法响应JVM的中断请求。

解决:

安全区域指在一端代码片段中,引用关系不会发生变化。可以看作扩展的 Safepoint

线程执行到 SafeRegion 时标识自己已经进入到 SafeRegion ,若这段时间JVM发起GC,就不用管这样状态的线程了。

在线程离开 SafeRegion 时检查系统是否已经完成根节点枚举,如果没有完成,必须等待收到可以离开 SafeRegion 的信号为止。

垃圾收集器

Serial 串行收集器

原理:

进行垃圾收集工作时,必须暂停其他所有工作线程,即GC线程需要和用户线程串行。

优点:

简单高效,Clent 模式下默认新生代收集器。

缺点:

Stop The Word 停顿时间长。

ParNew(颇牛) 多线程串行收集器

原理:

Serial收集器的多线程版本,GC线程并发执行,暂停所有用户线程。

特点:

ParNew作为新生代收集器 可以和 CMS老年代收集器搭配使用。

是使用 -XX:+UserConcMarkSweepGC选项后的默认新生代收集器,也可以使用 -XX:+UserParNewGC 选项使用它。

Parallel Scavenge 并行的多线程收集器

Parallel Scavenge 收集器目的是达到一个可控制的吞吐量,吞吐量优先收集器。

吞吐量就是CPU运行用户代码时间与CPU总消耗时间的比值。
吞吐量 = 用户代码时间 / ( 用户代码时间 + 垃圾收集时间 )

CMS等收集器关注点则在缩短停顿时间。

追求短停顿时间和追求可控吞吐量的区别?
GC停顿时间短是牺牲吞吐量和新生代空间来换取的。
停顿时间短,则可能GC停顿频繁,整体上用户代码时间缩短,吞吐量不高。
停顿时间短适合用户交互程序;提高吞吐量则高效率利用CPU时间,适合后台计算任务。

Parallel Scavenge 收集器提供了两个参数用于精确控制吞吐量。

  1. -XX:MaxGCPauseMillis 最大垃圾收集停顿时间,大于0。
  2. -XX:GCTimeRatio 直接设置吞吐量大小,大于0,小于100,默认99,允许最大1%的垃圾收集时间。
  3. -XX:+UserAdaptiveSizePoliy 自适应调节策略开关,使用最大停顿时间和吞吐量参数给虚拟机设立一个优化目标。

Serial Old 收集器

Serial 收集器的老年代版本,单线程收集器,使用 标记-整理 算法,Client模式下使用。

jdk1.5之前与 Parallel Scavenge 收集器搭配使用

作为CMS收集器后备预案

Parallel Old 收集器

Parallel Scavenge 收集器老年代版本,使用多线程和 标记-整理 算法

吞吐量优先 收集器组合 Parallel Scavenge + Parallel Old

CMS 收集器

一种以获取最短回收停顿时间为目标的收集器,基于 标记-清除 算法实现。

运作过程

  1. 初始标记

    需要 Stop The World , 仅仅标记 GC Roots 直接关联到的对象。

  2. 并发标记

    进行 GC Roots Tracing 的过程,标记引用链,与用户线程同时进行

  3. 重新标记

    需要 Stop The World , 修正并发标记期间因用户线程变动的标记。

  4. 并发清除

    对标记的内存回收,与用户线程同时进行。

缺点

  1. CMS 收集器堆CPU资源敏感。
  2. 无法处理浮动垃圾,可能 Concurrent Mode Failure失败,临时启用 Serial Old重新收集老年代。
  3. 产生大量的空间碎片,使用 -XX:+UseCMSCompactAtFullCollection开关参数,FULLGC时开启内存碎片整理。

G1 收集器

Garbage-First 收集器。

特点

  1. 并行与并发
  2. 分带收集
  3. 空间整合
  4. 可预测的停顿

运作过程

  1. 初始标记
  2. 并发标记
  3. 最终标记
  4. 筛选回收

gc日志

垃圾收集器参数

内存分配与回收策略

原文地址:https://www.cnblogs.com/hiqianqian/p/8349783.html

时间: 2024-10-14 04:43:18

jvm笔记-02-垃圾收集器与内存分配策略的相关文章

jvm系列 (二) ---垃圾收集器与内存分配策略

回顾 上文介绍了jvm的内存区域以及介绍了内存的溢出情况. jvm区域分为5个,线程独有:虚拟机栈,本地方法栈,程序计数器.线程共享:方法区,堆 两种溢出:栈溢出(StackOverflowError),OutOfMemoryError(OOM) 为什么学习垃圾收集 看起来jvm好像一切帮你做好,但是当垃圾收集成为系统达到更高并发量的瓶颈时,我们就需要对这种自动化的技术进行监控和调节. 根据实际应用需求,选择最优的收集方式才能更高的性能. 垃圾收集的区域 虚拟机栈,本地方法栈,程序计数器是线程私

Java虚拟机垃圾收集器与内存分配策略

Java虚拟机垃圾收集器与内存分配策略 概述 那些内存需要回收,什么时候回收,如何回收是GC需要完成的3件事情. 程序计数器,虚拟机栈与本地方法栈这三个区域都是线程私有的,内存的分配与回收都具有确定性,内存随着方法结束或者线程结束就回收了. java堆与方法区在运行期才知道创建那些对象,这部分内存分配是动态的,本章笔记中分配与回收的内存指的就是:java堆与方法区. 判断对象已经死了 引用计数算法:给对象添加一个引用计数器,每当有一个地方引用它,计数器+1;引用失败,计数器-1.计数器为0则改判

垃圾收集器与内存分配策略(五)之垃圾日志与常见参数

垃圾收集器与内存分配策略(五)--垃圾日志与常见参数 理解GC日志 每个收集器的日志格式都可以不一样,但各个每个收集器的日志都维持一定的共性.如下面二段日志: 33.125: [GC [DefNew: 3324K->152K(3712K), 0.0025925 secs] 3324K->152K(11904K), 0.0031680 secs] 100.667: [Full GC [Tenured: 0K->210K(10240K), 0.0149142 secs] 4603K->

垃圾收集器以及内存分配策略

垃圾回收 垃圾回收的三个问题: 哪些内存需要回收? 什么时候回收? 如何回收? 1.哪些对象需要回收? 判断对象是否存活的办法: 引用计数算法:给对象中添加一个引用计数器,有一个地方引用就+1,引用失效就-1.只要计数器为0则对象已死. 优点:简单易实现: 缺点:无法解决对象之间相互引用的问题.(JVM也因为此种原因没有使用它) 根搜索算法: 通过选取出一个GC Roots对象,已它作为起始点,如果对象不可达,则对象已死. GC Roots对象: 虚拟机栈中引用的对象 方法区中类静态属性引用的对

垃圾收集器与内存分配策略(三)之HotSpot的算法实现

垃圾收集器与内存分配策略(三)--HotSpot的算法实现 Java JVM 垃圾回收 在HotSpot虚拟机上实现这些算法时,必须对算法的执行效率有着严格的考量,才能保证虚拟机高效地运行. 1. 枚举根节点 采用可达性分析从GC Roots节点中找引用链为例 存在的缺点: 1.在前面找出还存活对象时,采用可达性分析从GC Roots节点中找引用链时,可作为GC Roots的节点主要在全局性的引用(方法区的常量或类静态属性引用)与执行上下文(虚拟机栈栈帧中的本地变量表或本地方法栈中的Native

垃圾收集器与内存分配策略(二)之垃圾收集算法

垃圾收集器与内存分配策略(二)--垃圾收集算法 Java JVM 垃圾回收 简单了解算法的思想 1. 标记-清除算法 标记-清除算法分为标记和清除二个阶段:首先标记出需要回收的对象(详见上一节的可达性分析找出存活对象),在标记完成后统一回收所有被标记的对象. 缺点: 1.标记和清除二个过程的效率都不高 2.空间问题,标记清除后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后再程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作. 2. 复制算法 复制算

深入理解java虚拟机----->垃圾收集器与内存分配策略(下)

1.  前言 内存分配与回收策略 JVM堆的结构分析(新生代.老年代.永久代) 对象优先在Eden分配 大对象直接进入老年代 长期存活的对象将进入老年代 动态对象年龄判定 空间分配担保  2.  垃圾收集器与内存分配策略 Java技术体系中所提倡的自动内存管理最终可以归结为自动化地解决两个问题: 给对象分配内存; 回收分配给对象的内存. 对象的内存分配,往大方向上讲就是在堆上的分配,对象主要分配在新生代的Eden区上.少数也可能分配在老年代,取决于哪一种垃圾收集器组合,还有虚拟机中的相关内存的参

第三章 垃圾收集器和内存分配策略

第三章 垃圾收集器和内存分配策略 对象已死吗 引用计算方法 可达性分析算法 通过一些列的GC roots 对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径成为引用链,当一个对象到GC roots 没有任何引用链的则证明对象不可用的 虚拟机栈中的引用的对象 方法区中类静态属性引用的对象 方法去区中常量引用的对象 本地方法栈中JNI引用的对象 生存还是死亡 一次筛选,筛选是否有必要执行 finalize()方法 没有覆盖或者finalize()已经被调用过  视为没必要执行 放入一个F-Qu

垃圾收集器与内存分配策略(二)

垃圾收集算法简介 1.标记-清除算法       标记-清除算法主要分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一进行回收.对象的标记过程在垃圾收集器与内存分配策略(一)中已经介绍过. 存在的问题:一是效率问题,标记和清除的效率都不高:二是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时无法找到足够的内存而不得不提前触发另一次垃圾收集动作. 2.复制算法       复制算法:它将内存按照容量划分为大小

垃圾收集器与内存分配策略(四)之垃圾收集器

垃圾收集器与内存分配策略(四)--垃圾收集器 收集算法是内存回收的方法论,垃圾收集器则是内存回收的具体实现. 垃圾收集器介绍 在垃圾收集器的层面上对并行与并发的解释: 并行(Parallel):指多条垃圾收集线程并行工作,但此时用户现场仍处于等待状态. 并发(Concurrent):指用户线程与垃圾收集线程同时执行(但并不一定是并行的,可能会交替执行),用户程序仍在继续执行,而垃圾收集程序运行于另一个CPU上. 对于不同的厂商,不同的版本的虚拟机都可能有很大的差别.此处讨论的是jdk1.7之后的