JVM概览

在这里插入图片描述

GC是什么?

清理jvm运行时数据区域-堆中的无用对象.

清理的是哪部分内存?

jdk1.7: 方法区,堆空间 1.8 : 堆空间,元空间.

为什么需要清理?

内存有限.

怎么清理?
判断对象是否需要被清理
  • 引用计数法

无法解决循环引用问题.

  • 可达性分析算法

从"GC Roots"的对象作为起始点,延引用链搜索.如果一个对象没有任何任何GCRoots能够访问到.那这个对象就是不可达状态.即可回收.

GC Roots
  • 虚拟机栈(栈中的本地变量表)中引用的对象.
  • 方法区中类静态属性引用的对象.
  • 方法区中常量引用的对象.
  • 本地方法栈中引用的对象.
引用状态
  • 强引用

Object a=new Object(); 在代码有效范围内时,对象永远不会被回收.

  • 软引用
SoftReference<String> stringSoftReference = new SoftReference<>("hello, World");

在jvm即将发生OOM的时候会回收这些对象.用作临时缓存.

  • 弱引用
WeakReference<String> weakReference = new WeakReference<String>(new String("hello, World"));

存活到下一次垃圾回收之前.

  • 虚引用
//必须配合引用队列来使用虚引用
ReferenceQueue<String> queue = new ReferenceQueue<String>();
PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue);
System.out.println(pr.get());
//===============
输出结果:
null

配合finalize函数使用,在finalize函数被调用后,虚引用就会被加入到引用队列中,可以通过检查引用队列判断对象的回收状态.

软引用和弱引用

如果有大量的数据要加载到内存中,有可能会造成OOM,造成其他业务异常,为了避免这种事情,可以使用软/弱引用降低OOM风险

常用GC算法
  • 标记-清除

    1. 标记出需要回收的对象.
    2. 清除被标记的对象

在这里插入图片描述

效率低, 产生大量不连续内存碎片.

  • 复制
    在这里插入图片描述

没有了内存碎片确要浪费一半的内存空间,而且如果存活对象很多的是否,会产生大量的复制工作. 代价太高.在young(新生代)中采用了8:1:1的比例分配eden,from,to,只浪费了young区的1/10;

  • 标记-整理
    在这里插入图片描述

因复制算法的浪费内存空间,大量复制对象特性,无法适用于老年代,标记整理算法就是为了补充复制算法的不足,但是还是存在对象复制的情况.

  • 分代收集

根据对象存活周期不同将内存区域划分: young,old, 然后对之前的算法进行一个适当的组合使用, 新生代对象死的快就适合用复制算法,可以减少存活对象的复制工作. 老年代对象存活率较高,需要空间比较大,无法提供很多的浪费空间不适合使用复制算法,可以使用标记-清除/标记-整理算法.

HotSpot 的算法实现
  • 判断对象的存活状态,在HotSpot中使用的是可达性分析算法

    1. 枚举根节点, 因为空间很大,不可能去检查每一个引用,通过一组OopMap的数据来判断对象的存活状态,在类加载完成的时候就维护了这个数据 ,JIT编译时,会在所有方法返回之前以及循环跳转、异常跳转之前放置Safepoint,并且为每个Safepoint都生成一些信息存储哪些地方是引用(OopMap).通过直接扫描这些维护好的数据就可以快速的寻找到存活的对象.

    可达性分析算法需要在一个一致性的快照中进行,因为运行中的系统引用关系在不停的发生变化,所以就需要将系统冻结在某个时间点.(Stop The World).

    1. Safepoint :

    内存分配的地方(allocation,即new一个新对象的时候)

    长时间执行区块结束的时刻(如方法调用,循环跳转等)

    之所以只在特定的位置放置safepoint,是因为OopMap要占用空间,如果设太多safepoint那么占用空间会太大;再者,safepoint会影响优化,如果某个无用的值处设置了safepoint,那么JIT就无法优化掉这些无用变量,这会影响性能。

    HotSpot JVM在通过JIT编译时,会在所有方法返回之前以及循环跳转、异常跳转之前放置Safepoint,并且在每个Safepoint都生成一些信息存储哪些地方是引用(OopMap),以便JVM能找到需要的引用。

    那么如何确保GC时所有线程都到达GC Safepoint呢?有两种方法:抢占式中断(Preemptive Suspension)和主动式中断(Voluntary Suspension)。

    抢占式中断不需要线程的执行代码去主动配合,当触发GC时,JVM会中断所有线程,然后依次检查每个线程中断的位置是否为Safepoint,如果不是则恢复线程,让它执行至Safepoint再进行终端。

    大部分JVM实现(如HotSpot JVM)都是采用主动式中断,即GC需要中断线程的时候,它仅仅简单地设个标志,执行线程会主动轮询这个标志位,如果标志位就绪的话就自行中断。

    1. SafeRegion:

    只有GC Safepoint是不足的,因为我们发现,有一种情况,线程无法响应JVM的中断请求,也无法去轮询标志位:

    线程处于阻塞或等待状态

    对于这种情况,引入了safe-region的概念。

    Safe-Region是指在代码片段中,引用关系不会发生变化,因此GC可以随心所欲地在任何地方执行。在线程执行到Safe Region里面的代码时,首先标识自己已经进入了Safe Region,那样当这段时间里JVM要发起GC,就不用管标识自己为Safe Region状态的线程了。在线程要离开Safe Region时,它要检查系统是否已经完成了根节点枚举(或者是整个GC过程),如果完成了,那线程就继续执行,否则它就必须等待直到收到可以安全离开Safe Region的信号为止。

    另外,当一个线程在执行native方法时,由于此时该线程在执行JVM管理之外的代码,不能对JVM的执行状态做任何修改,因而JVM要进入safepoint不需要关心它。所以也可以把正在执行native函数的线程看作“已经进入了safepoint”,或者把这种情况叫做“在safe-region里”。

常用垃圾收集器
新生代垃圾收集器
  • serial

jdk1.3.1之前的唯一收集器,单线程收集,并且全程StopTheWorld.

  • parNew

serial的多线程版本,多用于配合CMS(Current Mark Sweep)收集器工作.CMS算是HotSpot中第一个并发收集器(能和用户线程并行工作),在单核CPU中,serial效果比parNew好,减少线程切换的工作.它默认开启的垃圾收集线程数是和CPU核数相同,可以通过参数: -XX:ParallelGCThreads 限制最大线程数.

Parallel: 多条垃圾收集线程同时工作. 用户线程处于暂停状态.

Concurrent: 垃圾收集线程和用户线程同时工作.

  • Parallel scavenge

新生代收集器,和parNew类似的并行多线程收集器, 它关注的是系统的可控制的吞吐量(Throughput). 吞吐量=运行用户代码的cpu时间/(用户代码cpu时间+垃圾收集cpu时间),适用于高效利用cpu时间的场景(后台运算),不适合用于和用户交互的场景.两个参数控制吞吐量: 最大的系统停顿时间: -XX:MaxGCPauseMillis(时间减小是以缩短吞吐量和新生代空间为代价的,过小会导致系统频繁发生垃圾回收) 吞吐量大小: -XX:GCTimeRatio (0-100), -XX:+UseAdaptiveSizePolicy 自适应调节策略开关,系统会根据系统运行情况自动分配 eden,from,to,-XX:PretenureSizeThreshold(进入老年代的分界年龄).


老年代垃圾收集器
  • Serial old

Serial 的老年代版本收集器 Serial old.是个单线程收集器(标记整理算法),在jdk1.5以及之前用来配合Parallel scavenge收集器工作. CMS收集器的后备预案.CMS会发生Concurrent Mode Failure.

  • parallel old

parallel scavenge的老年代版本(标记整理算法),jdk1.6开始提供.用于和parallel scavenge配合工作.

  • cms

CMS(concurrent mark sweep): 是一款以获取最短回收停顿时间为目标的垃圾收集器,用于提高服务器响应速度.

使用的是标记清除算法.

工作四个步骤:

  1. 初始标记(initial mark): 仅仅标记gc roots能直接关联到的对象.工作量小,停顿时间短.
  2. 并发标记(concurrent mark): gc roots tracing, 和用户线程同时工作.
  3. 重新标记(remark): 修正上一步过程中用户产生的对象引用变动.工作量小,停顿时间短.
  4. 并发清除(concurrent sweep): 清理垃圾对象.

并发阶段由于会占用资源, 会降低系统的吞吐量;

无法处理浮动垃圾(floating garbage, 在并发清理阶段产生的垃圾) 可能会出现concurrent mode failure,从而产生另外一次full gc.因为并发清除阶段要预留一部分空间给用户线程使用,如果这个预留空间不足就会产生 concurrent mode failure错误,这时就启用serial old收集器来重新进行老年代的垃圾回收工作.这就会产生严重的stop the world. 预留空间阈值: -XX:CMSInitiatingOccupancyFraction值太高容易出现concurrent model failure错误,太低性能下降明显.

算法原因会导致内存碎片. 可以使用-XX:+UseCMSCompactAtFullCollection开关,默认开启.在full gc之前开启内存碎片整理.又会产生stop the world.

-XX:CMSFullGCsBeforeCompaction 不整理full gc次数后来一次整理过程.默认值为0,每次full gc 都进行内存碎片整理.

  • g1

特征

  • 并行并发

利用多核cpu特性缩短停顿时间, 利用并发特性让gc和用户线程同时工作

  • 分代收集

用不同的方式处理不同年龄的对象,以获得最佳的收集效果.

  • 空间整合

从整体上看是标记整理算法,从两个region中看是复制算法实现的.这两种算法都不会产生内存碎片.避免因为大对象的内存分配问题导致频繁发生下一次gc.

  • 可预测停顿

通过预设可接受停顿时间合理安排回收region.

工作原理图示
在这里插入图片描述

Region

​ G1和其他收集器的不同之处在于其将内存分为大小相同的若干个独立的 region,新生代和老年都是由多个不连续的Region组成.G1会维护一个垃圾堆积的价值大小排序的优先列表,每次根据允许的收集时间,优先回收价值(回收后所获得的空间大小,回收需要的时间的经验值)最大的Region,从而保证在最短的时间内最大化收集效率.可以通过参数: -XX:G1HeapRegionSize 1-32M,2的指数,如果不指定大小通过: size =(堆最小值+堆最大值)/ TARGET_REGION_NUMBER(2048) ,然后size取最靠近2的幂次数值, 并将size控制在[1M,32M]之间。

​ Region和Region之间发生引用的处理(在之前的分代收集中新生代和老年代也是如此): 在可达性分析中判断对象是否存活的时候必须要从根节点开始遍历所有节点,效率极其低下,为了解决这个问题虚拟机通过维护一个Remembered Set来避免全堆扫描,如图所示,在发生Reference 类型数据写操作时: b.a=a;检查a和b是否在同一个region中,如果不在,在a的Remembered Set中记录引用关系,在遍历GC root时,加入Remembered Set即可.

回收过程

  1. 初始标记
  2. 并发标记
  3. 重新标记
  4. 清除
  5. 转移回收

特性

  1. 根据设定的目标暂停时间自动调整新生代的空间大小和总堆大小
  2. 分区回收,将存活对象复制到新的空间分区, 天然压缩方案(局部)
  3. 混合收集模式,回收可能只包含新生代和同时包含新生代和老年代

Region

可以通过参数 -XX:G1HeapRegionSize=n( 1-- 32 之间 2 的次幂),默认是2048个region.

Card

Region的进一步细分,分为若干个512Byte大小的Card, Card为堆内最小粒度的可用内存. 所有Region的Card都记录在Global Card Table中,对象的分配都是分配在物理上连续的若干个卡片,每次的内存回收就是对指定Region的Card处理.

分代

G1的分代是逻辑上的分代,不需要固定的物理上连续内存作为新生代或者老年代,其大小是根据目标暂停时间动态调整的,新生代内存的初始空间会在-XX:G1NewSizePercent(默认整堆5%) 和最大空间 -XX:G1MaxNewSizePercent(默认60%)直接动态变化, 变化的依据是: 目标暂停时间-XX:MaxGCPauseMillis(默认200ms)/RSet计算得到. 不能设置固定大小的年轻代大小,如果设定目标暂停时间将没有意义.

LAB

Local allocation buffer ,本地线程和gc,promotion都有自己的独占region分别称作: TLAB,GCLAB,PLAB.

Humongous Region

在新分配对象的大小超过Region对象的一半大小时,因为大对象的复制成本极高,需要一开始就分配在老年代中,如果跨Region的时候需要寻找连续的Region,寻找物理上连续的Region就需要扫描整个堆,这种对象jvm会实时监测它的引用,如果不存在引用了,在新生代GC的时候,立马回收这种对象.(避免产生这种对象)

Remembered Set

在g1之前的垃圾收集器中,值可达性分析中都是stw扫描整个堆来确定是否可达, g1通过为每个region维护一个记忆集合(Rset),内部类似一个反向指针,记录了内部的对象被哪些外部Region中的对象所引用.(内部互相引用不需要记录在Rest中,新生代Region之间互相引用不用记录在Rset中因为g1的新生代每次gc都是整体gc),只有在和老年代之前发生引用时才会用到Rset.

Pre Region Table

Rset中通过PRT记录分区的引用情况,由于Rset需要占用Region的空间,为了控制Rset对Region可用空间的影响,PRT采用三种模式记录Region中的引用情况:

  1. 稀少: 直接记录Card的索引.
  2. 细粒度: 引用该对象的分区索引.
  3. 粗粒度: 记录被引用的分区数量.(找到所有的引用就需要扫描整个堆)

CSet

Collection Set, 就是每次回收的Region集合,如果是新生代回收会包含整个新生代的Region,如果是混合回收会包含全部的新生代和回收收益高的老年代Region.

CSet of Young Collection

发生JVM分配对象到Eden区,Eden区满的时候,发生整个young区域的gc, Eden区域中存活的对象复制到新的Survivor区域,原Survivor区域中的对象根据阈值移到PLAB(新的Survivor区,老年代分区)中.新生代整体回收.

CSet of Mixed Collection

老年代空间占用超过阈值( -XX:InitiatingHeapOccupancyPercent(默认45%)),g1会启动一次混合垃圾收集,通过多次回收一部分老年代Region达到控制STW时间在设定的停顿时间范围内.老年代回收Region是按照回收收益率高低排序.具体次数可以通过参数 -XX:G1MixedGCCountTarget(默认8) 控制.

Barrier

Pre-Write Barrier

在STAB日志或者缓冲区中记录丧尸引用的对象

Post-Write Barrier

在Rset log中记录对象被新引用的信息.

STAB

在并发标记阶段,STAB会创建一个对象图,相当于堆的逻辑快照,在标记的过程中发生的变化一部分会通过Pre-Write Barrier记录下来,在并发标记的同时会定期检查处理Pre-Write Barrier的记录日志来更新Rset.

Concurrence Refinement Threads

并发优化线程是一个永久活跃的线程,会一直监控Post-Write Barrier的记录,如果有记录就会立马并发处理更新Rset.如果记录更新过快有可能导致应用线程被暂停来处理此记录.

转移失败的担保机制 Full GC

转移失败(Evacuation Failure)是指当G1无法在堆空间中申请新的分区时,G1便会触发担保机制,执行一次STW式的、单线程的Full GC。Full GC会对整堆做标记清除和压缩,最后将只包含纯粹的存活对象。

  1. 从年轻代分区拷贝存活对象时,无法找到可用的空闲分区
  2. 从老年代分区转移存活对象时,无法找到可用的空闲分区
  3. 分配巨型对象时在老年代无法找到足够的连续分区
  • Zgc
  1. 暂停不超过10ms
  2. 暂停时间不会应为内存增大而边长.
  3. 能管理的内存范围广 KB-TB

并发GC

内存标记,复制,迁移操作是并发的.

内存不分代

ZGC依赖NUMA-aware(非均衡存储器访问),需要我们的内存支持这种特点

只能工作在64位的操作系统上.

新术语:

着色指针 Colored Pointer

读屏障Load Barrier

与标记对象的传统算法相比,ZGC在指针上做标记,在访问指针时加入Load Barrier(读屏障),比如当对象正被GC移动,指针上的颜色就会不对,这个屏障就会先把指针更新为有效地址再返回,也就是,永远只有单个对象读取时有概率被减速,而不存在为了保持应用与GC一致而粗暴整体的Stop The World。

  • OpenJ9

    Memory Management: Memory Allocator and Garbage Collection.

    策略

    1. balanced 单线程标记, 并行清除, 压缩`.

    2. gencon default

    3. metronome (AIX®, Linux® x86 only)

    4. nogc 不使用gc

    5. optavgpause 牺牲吞吐量优化暂停时间

    6. optthruput 牺牲暂时时间优化吞吐量

    策略选择: -Xgcpolicy: [policyName]

    Gencon

    启用:

    -Xgcpolicy:Gencon //j9的默认GC策略

在这里插入图片描述

其中Nursery space 又可以分为 Allocate和Survivor 两个sub spaces.

Allocate space 是内存分配区域

在Allocate space内存不足时发生local gc: 根据对象的年龄复制到survior 和 tenure space区域中.同时会根据tilting配置重新调整survior和allocate空间的比例.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值