JVM第一讲:JVM相关知识体系详解+面试(P6熟练 P7精通)

JVM相关知识体系详解+面试(P6熟练 P7精通)

面试时常常被面试官问到JVM相关的问题。本系列将给大家构建JVM核心知识点全局知识体系,本文是JVM第一讲,JVM相关知识体系详解和相关面试题梳理。

1、JVM学习建议

1.1、为什么学习 JVM?

  • 1、可以知道设备是怎么识别我们编写的Java程序的,规避它在使用中的 Bug;

  • 2、Java 虚拟机提供了许多配置参数,用于满足不同应用场景下,对程序性能的需求,你可以针对自己的应用,最优化匹配运行参数

  • 3、在面试中重要程度上:在当前 Java 后端面试中,JVM所有框架/中间件中被问到频率是最高的

1.2、学习的建议

  • 1、阅读源码。读源码其实也是一种实战锻炼,可以帮助你从代码逻辑中彻底理解 JVM 系统的实际运行机制。当遇到问题时,可以直接从代码层面进行定位、分析和解决问题
  • 2、亲自动手实践
    • 在开发中,使用并体会其特性
  • 3、学习的路线
    • JVM全景知识图
    • img
  • 4、重点学习内容:
    • 1、需了解内存模型各部分作用,保存哪些数据;
    • 2、类加载双亲委派加载机制,常用加载器分别加载哪种类型的类;
    • 3、GC分代回收的思想和依据以及不同垃圾回收算法的回收思路和适合场景;
    • 4、性能调优常有JVM优化参数作用,参数调优的依据,常用的JVM分析工具能分析哪些问题以及使用方法;
    • 下图主要表示的逻辑关系,用来将所有知识点放到一张图里,帮助你理解。
      • JVM整体流程:编译器将Java代码编译为字节码 --》类加载器将字节码加载到内存(加载、连接、初始化) --》放到方法区 --》字节码是一套规范,需要特定的解析器翻译成操作系统底层系统指令,然后让CPU执行 --》这个过程中也需要调用其他语言的本地库接口实现整个功能
      • 在这里插入图片描述

1.3、JVM 学习资料

参考的书籍 (帮助解决别人的JVM问题是学习JVM的最好方法)

1.4、项目中用到JVM的地方 (面试常见题目梳理)

1、运行时数据区?

场景1:方法区、堆内存以及栈内存

  • 方法区:存放类信息、常量池、静态变量
  • 堆内存:存放实例对象、数组
  • 栈内存:虚拟机栈 本地方法栈
  • 程序计数器:为了线程切换后能恢复到正确的执行位置

场景2:内存分配策略**,**对象的创建/布局/访问

  • 栈: 基础数据类型,方法形参,对堆内存数据的引用
  • 堆:引用数据类型 数组
  • 对象的创建
  • 对象头信息(对对象进行必要的设置)
  • 对象的访问定位:通过栈上的引用数据来操作堆中具体对象

场景3:JVM指令重排以及内存屏蔽

  • 定义了一套程序中变量的访问规则
  • 提供对原子性,可见性,有序性保证

场景4:内存溢出,内存泄露

  • 什么是内存溢出?
    • 堆 场景1:对象创建太多超出了最大堆容量限制
    • **虚拟机栈和本地方法栈 ** 场景1:递归太深、死循环导致栈帧创建过多
    • 方法区和运行时常量池 场景1:方法区被填满
  • 项目中的内存溢出
    • 场景1、内存中加载的数据量过于庞大 持久层没有limit限制,导致大批量数据被查询
    • 场景2、代码中存在**死循环 **
      • dubbo 泛化调用时,使用姿势不对,导致了循环调用;
      • json序列化使用姿势不对,也会导致循环调用
  • 什么是内存泄漏
    • 一个对象已经不需要再使用,本该被回收,然而另外一个正在使用的对象持有它的引用从而导致它不能被回收
  • 项目中的内存泄漏?
    • 场景1:对象连接资源未关闭 网络资源、IO资源
    • 场景2:ThreadLocal 使用完毕没有调用remove方法
    • 场景3:guava cache 的weakKey 导致内存泄露
2、服务器使用的什么垃圾收集器?CMS 垃圾收集的原理?G1 垃圾收集的特点,为什么低延迟?有哪些垃圾回收算法,优缺点?
  • 有哪些垃圾回收算法,优缺点?

    • 复制算法 (新生代) 缺点:堆内存使用率低,只有原来的一半
    • 标记-清除算法 直接回收不存活的(老年代)
      • 缺点:分配效率较低
      • 空间问题:标记清除后会产生大量不连续的内存碎片
    • 标记 - 整理算法(老年代)
      • 优点:1、解决大量内存碎片问题;2、当对象存活率较高时,效率也很好
        缺点:压缩算法的性能开销大
  • 服务器使用的什么垃圾收集器? CMS(老年代)PraNew(新生代) G1

  • CMS 垃圾收集的原理?使用标记-清除算法(底层为三色标记法)

    • 初始标记 对应三色标记:gc-roots直接关联对象置为灰色
    • 并发标记 对应三色标记:没有子节点,将本节点变为黑色。有子节点:则当前节点变为黑色,子节点变为灰色
    • 最终标记 对应三色标记:此时黑色对象就是存活的对象,白色对象就是已消亡可回收的对象;
    • 并发清除 清理未使用的对象并回收它们占用的空间
  • G1 垃圾收集的特点,为什么低延迟?

    • 使用G1收集器时,将堆划分为相等的region,并且能和整个堆中任意的对象发生引用关系,优先回收价值大的region。每个Region都有一个Remembered Set,用来记录该Region对象的引用对象所在的Region。通过使用Remembered Set,在做可达性分析的时候就可以避免全堆扫描。
    • 过程
      • 1、初始标记(STW) 标记GC roots对象,不会立即执行,而是等到下次young gc时执行,尽量减少STW
      • 2、并发标记
        • 进行GC Roots Tracing的过程,扫描整个堆,进行可达性分析
        • 耗时较长,没有停顿
      • 3、最终标记(STW)
        • 找出并发标记阶段,未被标记的存活对象
      • 4、筛选回收(STW)
        • 根据停顿时间来进行价值最大化的回收(默认200ms)

        • RSet梳理

        • 对old regions进行对象存活率排序,并放入 Cset

        • 识别空闲分区,即无存活对象的分区,立即回收

  • G1收集器的设计目标是取代CMS收集器,它同CMS相比,在以下方面表现的更出色

  • 1、并发标记时间

    • 由于CMS需要并发标记整个old区,所以极端情况下(大堆),标记时间会很长,
    • 而G1会选择性的选择GC region。G1在停顿时间上添加了预测机制,用户可以指定期望停顿时间,所以G1时间更可控
      • 该收集器是把整个堆(新生代、老年代)划分成多个固定大小的区域,每次根据允许停顿的时间去收集垃圾最多的区域。
      • G1更优
  • 2、内存碎片

    • CMS使用标记-清除法,会产生内存碎片,若触发内存碎片整理,也会加长GC时间。

      • G1采用复制算法,不存在很多内存碎片情况(未考虑大对象场景)。
        • G1更优
  • 3、浮动垃圾

    • CMS最后一步: 并发清除会产生浮动垃圾

      • CMS更优
    • 相对而言G1只会选择性的选择region,故G1浮动垃圾更严重。

  • 4、垃圾收集时的内存占用

    • G1无论是为了垃圾收集产生的内存占用还是程序运行时额外执行负载都要比CMS高。

    • 优劣势的平衡点:6GB~8GB间

3、哪些对象可以作为 GC Roots?
  • 堆外指向堆内的引用

    • 1、虚拟机栈中引用的对象(堆中);

      • Java堆从GC的角度还可以细分为:新生代(Eden 区、From Survivor 区和To Survivor 区)和老年代。
    • 2、本地方法栈 native方法引用的对象;

    • 3、方法区中 类静态属性 引用的对象;

    • 4、方法区中常量引用的对象。

4、有哪些类加载器?双亲委派模式,哪些场景是打破双亲委派模式?
  • 类加载
    • 作用:类加载器将字节码加载到内存,并对数据进行校验,解析和初始化,最终形成可被虚拟机直接使用的 java 类型。
    • 过程:加载、链接、初始化
    • 四种类加载器:启动类加载器 扩展类加载器 系统类加载器 用户自定义类加载器
      • 目前没有使用场景
      • 双亲委派模型 先让最上层去加载
    • 哪些场景是打破双亲委派模式? 重点
      • 我知道有两种方式来破坏双亲委派模型
        1. 第一种,继承ClassLoader抽象类,重写loadClass方法,在这个方法可以自定义要加载的类使用的类加载器
        2. 第二种,使用线程上下文加载器,可以通过java.lang.Thread类的setContextClassLoader()方法来设置当前类使用的类加载器类型。
          1. java.jdbc.Driver(SPI) Tomcat容器,也存在破坏双亲委派的情况,来实现不同应用之间的资源隔离
          2. tomcat自定义了类加载器,重写loadClass方法使其优先加载自己目录下的class文件,来达到class私有的效果
5、线上服务器出现频繁 Full GC,怎么排查?介绍下 JVM 调优的过程?

GC与调优经验 消息推送出现了数据洪峰 即10W条数据在短时间内(10分钟)推送

  • 场景1:哪些内存需要回收? 方法区

  • 场景2:什么时候回收?在堆内存不足时触发,使用可达性算法判断对象是否存活

  • 场景3:如何回收死亡的对象?(使用分代收集思想)

    • 新生代:Serial、PraNew(配合CMS使用)、Parallel Scavenge
      • 复制算法 新生代空间使用
    • 老年代:Parallel Old、CMS
      • **标记-清除算法 ** CMS 目标:最短回收停顿时间
      • 如何解决STW:将GC过程细化,区分出必须STW场景和非必须STW场景。从业务场景出发解决问题
      • 缺点1:标记-清除算法无法清理浮动垃圾
      • 缺点2:并发清理可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生
    • 整堆:G1
      • 标记 - 整理算法 G1 不会产生内存空间碎片
      • 目标:实现指定的GC暂停时间,同时还能保持较高的吞吐量
      • 重点概念:卡表:标记老年代是否存在对新生代的引用
      • G1适用场景
        • 追求RT:对外提供服务的服务器
        • GC耗时严重:大堆jvm
        • 多核服务器(并发GC)
    • 使用场景1:新生代使用 parNew ,老年代使用 CMS
    • 使用场景2:整堆使用G1 目前来说G1属最佳实践
  • 为什么有两个幸存空间? 解决碎片问题

    • 新生代 ( Young )和老年代( Old ) 的比例:默认比例1:2
    • 遇到什么场景,需要调整这个比例?
  • 场景4:什么时候触发young GC? 新生代

    • ①eden区满;②新创建的对象大小 > Eden所剩空间
    • 经验值:YGC耗时在100ms以内,50ms以内尤佳
  • 场景5:什么时候触发F GC? 老年代

    • ①内存分配速度大于 YGC速度;②空间分配担保失败;③大对象分配失败;④设置了参数 -XX:CMSInitiatingOccupancyFaction,老年代可用内存大于历次年轻代GC后进入老年代的对象的平均大小,但是老年代已使用内存超过该参数指定的比例,自动触发Full GC
    • 经验值:FGC最多几小时1次,1天不到1次尤佳,耗时在1s以内,500ms以内尤佳
  • 场景6:调优经验,主要是做过一些什么样的调优?

    • 1、为啥调优:如果使用合理的JVM参数设置,在大多数情况下应该是不需要调优的,少量场景需要调优,配置监控告警 工具:使用Arthas dashboard
      • 查看当前 JVM 堆内存参数配置是否合理
      • 查看堆中对象的统计信息
      • 查看堆存储快照,分析内存的占用情况
      • 查看堆各区域的内存增长是否正常
      • 查看是哪个区域导致的GC
      • 查看GC后能否正常回收到内存
    • 我们的场景:消息推送出现了数据洪峰 即10W条数据在短时间内(10分钟)推送
      • 限流方案是:推送平台通过消费MQ,执行职责链进行数据推送,根据业务类型在当前推送队列中的数量进行限流
      • 原因:网络请求跟不上上游MQ的投递速度,导致内存逐渐被打满了;
      • 瓶颈:在于JVM 堆内存参数配置不合理
    • 我们的目标:尽可能降低GC频率和耗时
    • 方案:
      • 1、JVM方面,调大年轻代大小:由1G调整为 1.5G eden与serviver比例:10:1:1修改为8:1:1,避免年轻代过多进入老年代
      • 2、代码方面:修改限流器,设定一个jvm堆内存的使用率,当超过这个阈值后对当前的消费线程进行阻塞,直到使用率低于阈值后再进行放行并且设置一个最大阻塞时间,超过该时间后放行该消费者线程
    • 结果:性能提升了一倍
      • 优化前 推送花了35分钟,fullGC 312次 单次耗时 991ms
      • 优化后 推送花了18分钟,fullGC 104次 单次耗时 436ms
6、定位问题常用哪些命令?
  • 使用 jstack 分析cpu问题;
  • 使用jstat -gc pid 1000命令来对gc分代变化情况进行观察;
  • 使用 dashboard -i 10000 -n 3命令来对gc分代变化情况进行观察;
  • iostat -d -k -x来进行分析磁盘问题;
  • 使用JMAP定位代码内存泄漏 jmap -dump:format=b,file=filename pid来导出dump文件;
  • 通过pmap来查看下进程占用的内存情况pmap -x pid | sort -rn -k3 | head -30

2、相关文章

2.1、JVM相关 - 知识体系

首先按照上述学习思路,理解总体知识点在全局上与知识体系之间的对应关系。

2.2、JVM相关 - 类加载

理解类字节码和类的加载机制。

  • JVM基础 - 类字节码详解
    • 源代码通过编译器编译为字节码,再通过类加载子系统进行加载到JVM中运行
  • JVM基础 - 字节码的增强技术
    • 在上文中,着重介绍了字节码的结构,这为我们了解字节码增强技术的实现打下了基础。字节码增强技术就是一类对现有字节码进行修改或者动态生成全新字节码文件的技术。接下来,我们将从最直接操纵字节码的实现方式开始深入进行剖析。
  • JVM基础 - Java 类加载机制
    • 这篇文章将带你深入理解Java 类加载机制

2.3、JVM相关 - 内存结构

因为类字节码是加载到JVM内存结构中的,所以紧接着理解JVM内存结构。

2.4、 JVM相关 - JMM

通过理解JVM与硬件之间的联系,理解Java 通过其内存模型保证数据线程安全等,这是JVM在并发上底层的支持。

  • JVM基础 - Java 内存模型引入
    • 很多人都无法区分Java内存模型和JVM内存结构,以及Java内存模型与物理内存之间的关系。本文从堆栈角度引入JMM,然后介绍JMM和物理内存之间的关系, 为后面JMM详解, JVM 内存结构详解, Java 对象模型详解等铺垫。
  • JVM基础 - Java 内存模型详解
    • 本文主要转载自 Info 上深入理解Java内存模型, 作者程晓明。这篇文章对JMM讲的很清楚了,大致分三部分:重排序与顺序一致性;三个同步原语(lock,volatile,final)的内存语义,重排序规则及在处理器中的实现java 内存模型的设计,及其与处理器内存模型和顺序一致性内存模型的关系

2.5、JVM相关 - GC

理解Java GC机制,如何回收内存等。

  • GC - Java 垃圾回收基础知识
    • 垃圾收集主要是针对堆和方法区进行;程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后也会消失,因此不需要对这三个区域进行垃圾回收。
  • GC-深入理解java虚拟机之垃圾回收算法?CMS垃圾回收的基本流程?对象引用类型?
    • 本文讲解讲解常见的垃圾收集算法,首先思考三个问题:1、哪些内存需要回收?2、什么时候回收?3、如何回收? 然后讲解内存回收的具体实现-垃圾收集器 ,最后对Java中 对象引用类型及具体使用场景 做了探讨,jvm的自动垃圾回收策略使得程序员摆脱了编程中繁杂的内存管理,可以把精力专注于系统业务。
  • GC - Java 垃圾回收器之G1详解
    • G1垃圾回收器是在Java7 update 4之后引入的一个新的垃圾回收器。同优秀的CMS垃圾回收器一样,G1也是关注最小时延的垃圾回收器,也同样适合大尺寸堆内存的垃圾收集,官方在ZGC还没有出现时也推荐使用G1来代替选择CMS。G1最大的特点是引入分区的思路,弱化了分代的概念,合理利用垃圾收集各个周期的资源,解决了其他收集器甚至CMS的众多缺陷
  • GC - Java 垃圾回收器之ZGC详解
    • ZGC(The Z Garbage Collector)是JDK 11中推出的一款低延迟垃圾回收器, 是JDK 11+ 最为重要的更新之一,适用于大内存低延迟服务的内存管理和回收。在梳理相关知识点时,发现美团技术团队分享的文章 新一代垃圾回收器ZGC的探索与实践比较完善(包含G1收集器停顿时间瓶颈,原理,优化等), 这里分享给你,帮你构建ZGC相关的知识体系。
  • GC - Java 垃圾回收器之CMS GC问题分析与解决
    • 本文整理自美团技术团队, 这篇文章将可以帮助你构建CMS GC相关问题解决的知识体系,分享给你。

2.6、JVM相关 - 排错调优

最后围绕着调试和排错,分析理解JVM调优参数,动态字节码技术及动态在线调试的原理;学会使用常用的调试工具和在线动态调试工具等。

img

3、参考文档

  • 42
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员 jet_qi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值