资讯专栏INFORMATION COLUMN

对比JVM GC算法的读书笔记

FrozenMap / 2173人阅读

摘要:但是为了丰富引用的种类,以适应各种应用,中加入了中引用,但是除了强引用,其生命周期会有所不同,生存能力递减。加载该类的已被回收。

GC面临的问题有三个:哪些内存需要回收、什么时候回收和怎么回收

哪些内存需要回收,一般有两种方法

引用计数

对每个对象都有个被引用的次数,单被引用的次数为0的时候,就表示对象需要被回收

引用计数的缺点是没有办法解决循环引用导致的内存泄露问题

可达性分析

现在主流的GC方法都使用可达性分析

以GC root为起点,遍历整个树(或者图),如果没有到达过某个对象,则表示这个对象需要被回收

GC Root表示引用链的起点,包括函数调用栈中引用的对象、static静态变量和常量的引用对象、方法区中常量引用的对象,本地方法栈中JNI引用的对象

也可以分为栈中的对象、永久代的对象、本地方法栈中JNI引用的对象

object什么时候会被回收

一般而言,只要没有引用指向object,就可以在gc的时候被回收(老年代需要在Full GC,永久代需要在设定的几次Full FC)。但是为了丰富引用的种类,以适应各种应用,JDK1.2中加入了4中引用,但是除了强引用,其生命周期会有所不同,生存能力递减。

强引用(Strong Reference)

即普通引用

软引用(Soft Reference)

当触发OutOfMemoryException之前,会触发第二次GC,回收这些Object

弱引用(Weak Reference)

当触发GC的时候,回收这些Object

虚引用(Phantom Reference)

对Object的生存无意义,当Object被回收时,会触发虚引用的通知

对已object在被回收的时候,还会有一个特例,就是定制Object的finalize方法,使之重新与GC Root关联,可以使Object逃脱回收

finalize方法在object被GC的时候会被执行

但是下次被GC的时候,不会再次执行finalize方法(finalize方法在生命周期内只会执行一次),所以不会再次逃脱回收

方法区或者Hotspot的永久代也会进行GC

普通常量池常量只要没有引用指向它,就可以被回收

类的回收判定需要满足3个条件

堆中不存在该类实例,即没有指向该类的引用(按照jvm的内存布局,类实例是会有指向类信息的pointer或者handler的。

加载该类的class loader已被回收。(class loader应该存在操纵其加载的类的方法,即还存在某种联系)

该类对应的java.lang.Class对象没有在任何地方被引用,在任何地方无法通过反射访问该类的方法(如果存在这种引用,则可以通过反射的机制,进行访问该类,这会导致错误,有点像race condition,持有缓存,内容却被改变了)

由这3个条件应该可以推论出:类实例、class loader、对应的java.lang.Class类和类信息都存在某种联系,使得可以通过这些东西操纵或者访问类信息。

对于内存怎么回收的问题,内存的回收算法一般分为三种

标记-清除

先对需要进行回收的内存进行标记,然后在进行清除

复制算法

将内存分为相等的两块,每次只使用其中一块,当当前使用的内存块使用完了之后,将存活的对象复制到另一内存块

Hotspot将新生代分为一个Eden和两个Survivor区,默认比例为8:1:1

minor gc的时候,将eden去和当前survivor区的存活对象复制到另一块survivor区

如果survivor区不足以放置存活的object,使用分配担保机制,部分object将直接进入老年代

标记-整理

先对存活的object进行标记,然后将object进行整理(统一往一端移动),最后将剩余的内存进行回收

分代收集

就是将内存分为不同的区域,一般分为新生代和老年代两部分,然后在不同的部分应用不同的收集算法

一般来说新生代使用复制算法,因为一般只会有少量object存活

老年代使用标记-清除或者标记-整理算法,因为对象存活率高,没有另外的内存进行分配担保

Hostspot的算法实现

枚举根节点

为了避免race condition的问题,这里需要进行stop the world的操作,保证一致性

为了提高标记的效率,降低stop the world的时间,商业vm一般都会使用准确式GC,会使用一些方法记录下引用的准确位置,避免全局内存扫描

Hostspot使用的是OopMap的数据结构来达到这个目的

OopMap的具体使用方法还需要深入了解

安全点

由于能导致OopMap变化的指令非常多,所以Hostspot设置安全点,只有在安全点才会生成OopMap,这也导致只有在安全点才能停顿下来进行GC

如何使所有线程进入安全点的方法有两个

抢先式中断,基本上不用

先把所有线程中断,如果线程不在安全点,则恢复线程让它到达安全点

主动式中断

设置标记位,标记是否正在进行根节点枚举

在安全点轮询标记位

安全区域

如果线程不运行,如sleep或者blocked状态,安全点就没法解决问题

安全区域指在这个区域内,代码引用关系不会变化

当线程到达安全区域时,会对自己进行标记,GC时,就不需要管安全状态的线程

当线程需要离开安全区域,需要检查根节点枚举标记位

垃圾收集器

Serial收集器

简单粗暴的串行收集器

适合内存不大,单CPU(可以避免线程交互开销),对stop the world不太敏感的client环境

ParNew收集器

Parallel New Generation,新生代并行收集器

Parallel Scavenge收集器

为控制吞吐量而生

通过控制stop the world的最大时间和gc时间的最大比例来控制gc时间,控制吞吐量

也可以使用自适应参数

算法主要控制stop the world的时间,但是代价是更频繁的gc和总体更长的gc时间总和

Serial Old收集器

老年代的串行收集器

使用的是标记-整体算法

Parallel Old收集器

老年代的并行收集器

和Parallel Scavenge收集器一起使用,应用于注重吞吐量和CPU敏感的场合

CMS收集器

Concurrent Mark Sweep,Mark Sweep,基于标记-清除算法

分为初始标记、并行标记、重新标记、并发清除4个阶段

其中初始标记和重新标记 都会stop the world

耗时最长的并发标记和并发清除都可以和用户线程一起运行

缺点

并CPU资源非常敏感,并发情况下,占用部分CPU资源,会导致吞吐量下降

无法处理浮动垃圾,可能出现“concurrent mode failed”而导致另一次Full GC

浮动垃圾,在并发清理阶段出现的垃圾,没法当次GC清除

需要预留老年代空间,给GC时,用户程序使用

标记-清除算法会导致内存碎片

可以设置是否在Full GC时整理内存,多少次Full GC整理一次内存

G1收集器

Garbage First

优点有:

并行与并发

分代手机

空间整合

整体来看是标记-整理算法,局部来看是标记-复制算法

可预测的停顿

通过设置GC时间的不得超过一定比例,几乎是实时Java(RTSJ)垃圾收集器的特征

G1实现

G1将内存分为多个相等的区域

老年代、新生代的概念还存在,但是改为了内存区域的集合,而不是固定的区域

G1跟踪各个Region区域里面垃圾堆积的价值(即花费时间回收内存的性价比),维护优先列表

由优先列表建立回收时间预测模型,优先收集性价比高的垃圾

由于分区域回收内存,G1的区域之间相互引用,导致可达性分析耗时的问题相比之前的收集器显得更加突出了

通过remember set来避免全堆扫描

remember set记录被其它区域引用的情况(待补充)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/65561.html

相关文章

  • 读书笔记JVM垃圾收集与内存分配策略

    摘要:堆和方法区只有在程序运行时才能确定内存的使用情况,垃圾回收器所关注的主要就是这部分内存。虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整比率参数以提供最合适的停顿时间或最大的吞吐量。 Tip:内容为对《深入理解Java虚拟机》(周志明 著)第三章内容的总结和笔记。这是第一次拜读时读到的一些重点,做个分享,也为后面再次阅读和实践做保障。 3.1 概述 程序计数器、虚拟机栈、本地...

    mcterry 评论0 收藏0
  • 《深入理解JVM读书笔记

    摘要:抽时间重新读了一遍深入理解一书。验证确保文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。可见性可见性是指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。 抽时间重新读了一遍《深入理解JVM》一书。以下为摘录内容。 1 java内存区域 showImg(https://segmentfault.com/img/bVboDgk?w=617&h=365...

    idealcn 评论0 收藏0
  • 读书笔记之深入理解Java虚拟机

    摘要:前言本文内容基本摘抄自深入理解虚拟机,以供复习之用,没有多少参考价值。此区域是唯一一个在虚拟机规范中没有规定任何情况的区域。堆是所有线程共享的内存区域,在虚拟机启动时创建。虚拟机上把方法区称为永久代。 前言 本文内容基本摘抄自《深入理解Java虚拟机》,以供复习之用,没有多少参考价值。想要更详细了解请参考原书。 第二章 1.运行时数据区域 showImg(https://segment...

    jaysun 评论0 收藏0
  • Java学习路线总结,搬砖工逆袭Java架构师(全网最强)

    摘要:哪吒社区技能树打卡打卡贴函数式接口简介领域优质创作者哪吒公众号作者架构师奋斗者扫描主页左侧二维码,加入群聊,一起学习一起进步欢迎点赞收藏留言前情提要无意间听到领导们的谈话,现在公司的现状是码农太多,但能独立带队的人太少,简而言之,不缺干 ? 哪吒社区Java技能树打卡 【打卡贴 day2...

    Scorpion 评论0 收藏0
  • JVM 工作原理,层次结构 以及 GC工作原理

    摘要:执行引擎作用执行字节码,或者执行本地方法运行时数据区其实就是指在运行期间,其对内存空间的划分和分配。 虽是读书笔记,但是如转载请注明出处https://uestc-dpz.github.io..拒绝伸手复制党 JVM Java 虚拟机 Java 虚拟机(Java virtual machine,JVM)是运行 Java 程序必不可少的机制。JVM实现了Java语言最重要的特征:即平台...

    qqlcbb 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<