当前位置: 首页 > news >正文

Jvm优化高手-笔记

语雀完整版:

https://www.yuque.com/g/mingrun/embiys/qod52e/collaborator/join?token=q1sDzh8tBGBqkX6M&source=doc_collaborator# 《Jvm优化高手》

从 0 开始带你成为JVM实战高手

第一周

001、开篇词: JVM优化实战,广大Java工程师心中永远的痛

需要实战

002、一探究竞:我们写的Java代码到底是如何运行起来的

  1. 使用java -jar 命令 启动的是一个jvm进程,将class文件 通过类加载器加载进jvm内存中,最后一步是jvm通过自己的 字节码执行引擎,来执行我们的代码。

003、面试官对于JVM类加载机制的猛烈炮火,你能顶住吗

  1. jvm 在什么情况下会加载一个类:在需要用到的时候,比如要执行一个主方法,那么要加载这个方法所在的类

一个类从加载到使用  需要经过  加载->验证->准备->解析->初始化->使用->卸载

  1. 验证 准备 解析
    验证:加载字节码的时候验证,还有运行的时候会验证
    准备:给这个类分配一些内存空间,给静态成员一个默认值
    解析:将符号引用化成直接引用
  1. 初始化
    干了什么:初始话阶段会执行静态代码(块),给静态成员变量赋值
    什么时间会初始化一个类:
    new 一个对象就可以触发,其中如果父类还没加载 会先加载父类

类加载器 和双亲委派机制

  1. Bootstrap ClassLoader 是加载lib目录下的核心类库。
    Extension ClassLoader 是加载 lib\ext下的 目录中的类
    Application ClassLoader 施加在classparth 里面的类,其实也就是自己的写的类
    另外可以自定义

  1. 可以对字节码加密,然后使用自定义的类加载器

004、大厂面试题: JVM中有哪些内存区域,分别都是用来干嘛的

  1. 类加载进Jvm之后 要有不同的区域来存放方法、变量
  1. 存放方法的区域: jdk1.8 之前叫方法区,里面还有一个常量池,1.8之后叫元空间,主要还是类的信息。

  1. 程序计数器:class进入jvm之后 它包含的各种指令 就会被字节码执行引擎执行,而多个线程 每个线程都会有自己的程序计数器,用来记录当前执行到的位置。

  1. 虚拟机栈:
    每个方法 都会有一个线程来执行,每个方法中包含的还会有变量,这些变量就存储在java虚拟机栈中,为每个线程所有。
    调用了一个方法 就会对这个方法调用创建一个栈帧,也就是执行一个方法就入栈,执行完后就出栈。
    栈帧中有 局部变量表 、操作数栈、动态链接、方法出口 。

  1. Java堆内存:
    new 一个对象,对象的实例数据就会存储在堆内存中,并且创建这个方法的虚拟机栈中 也会存储指向这块内存的引用地址
  1. 总结:
    main 方法执行, 先关联一个程序计数器,记录执行到了哪儿,然后压入一个人栈帧,如果创建对象则在堆中创建一块区域来存储对象实例,执行完成后在出栈。
  1. 其它区域: native 区,还有nio allcateDirect 堆外区域
  1. tomcat的类加载机制:他自定义了许多类加载器,并且打破了双亲委派机制

005、JVM的垃圾回收机制是用来干嘛的?为什么要垃圾回收

  1. 对象分配与引用:创建一个对象,对象存储在堆内存,栈帧中存储对象的引用地址,也就是栈帧中存储这个局部变量,而方法调用结束后就会出栈,释放掉变量
  1. 堆中存储的 对象占用一定空间,必须要按时清理掉,Jvm会一个线程把他们清理掉

006、第1周作业:不借助任何资料,画出JVM整体运行原理图

007、第1周答疑:本周问题统一答疑

  1. 不要在循环内 创建一个实例,可以在循环外创建,然后循环内修改该实例数据
  1. 类在方法去 加载类的时候必须要先执行静态代码
  1. 双亲委派机制 就是为了防止加载重复,如果都在一个层级 那么就会造成重复加载
  1. 自己的类 是什么时候用什么时候加载,而像 jre\lib\rt.jar 是系统核心 会先加载

第二周

008、聊聊JVM分代模型:年轻代、老年代、永久代

  1. 大部分对象的生命周期很短,少部分很长,而分成老年代和年轻代 也是为了使用不同的算法
  1. 永久代 就是方法区,方法区也会被回收,并且需要满足下面三个条件:
    首先该类的所有实例对象都已经从Java堆内存里被回收
    其次加载这个类的ClassLoader已经被回收
    最后,对该类的Class对象没有任何引用

009、大厂面试题:你的对象在JVM内存中如何分配?如何流转的

Minor GC/young GC:新生代垃圾回收,如果躲过了 15次回收就进入老年代

010、 动手实验:自感受一下线上系统部署时如何设置JVM内存大小

  1. 几个核心参数:
-Xms:Java堆内存的大小
-Xmx:Java堆内存的最大大小
-Xmn:Java堆内存中的新生代大小,扣除新生代剩下的就是老年代的内存大小了
-XX:PermSize:永久代大小  -> 1.8之后  -XX:MetaspaceSize
-XX:MaxPermSize:永久代最大大小 -> 1.8之后  -XX:MaxMetaspaceSize
-Xss:每个线程的栈内存大小

011、案例实战:每日百万交易的支付系统,如何设置JVM堆内存大小

  1. 支付系统背景

  1. 核心支付流程

  1. 100w订单:每个订单的创建在Jvm中,牵扯到的问题:
    多少台机器、每台多大内存、每台jvm给多少内存、怎么保证不崩溃
  1. 一个订单 需要一秒处理,一台服务器同时处理30个,总共三台服务器,一个订单估算500字节,30个订单15kb,  那么一秒钟 一台服务器 30个订单,就占用  30 * 500字节 = 15000字节,大概15kb,然后这个过程还会创建其它对象,需要把这个结果扩到10~20倍,那么每秒创建的对象就大概是 几百kb到1m左右
  1. 机器采用4核8G,然后-Xms和-Xmx设置为3G,给整个堆内存3G内存空间,-Xmn设置为2G,给新生代2G内存空

012、案例实战:每日百万交易的支付系统,JVM栈内存与永久代大小又该如何设置

  1. 有些请求的时间长,对象存活的时间长,老年代积压大量的对象,频繁触发老年代的回收,而老年代的回收比较慢。
  1. 永久代 一般设置几百MB,主要存放类的信息, 占内存一般设置 512KB或者1MB

013、第2周作业:看看你们的线上系统是如何设置JVM内存大小的

014、第2周答疑:本周问题答疑,上周作业点评

  1. 栈帧中的数据是出栈就没了  没有什么垃圾回收算法
  1. 就说是两个类 来自于同一个class文件,被同一个虚机加载,但是加载器不同的话 两个类就必定不同
  1. 双亲委派的意义:拿Object类来举例,如果没有双亲委派,那么就可能造成 你加载我也加载

第三周

015、大厂面试题:什么情况下JVM内存中的一个对象会被垃圾回收

  1. 可达性算法:从一个对象一层一层向上找,砍有谁在引用他,看是否有一个GC Roots
  1. 谁可以被当作GC Roots:
    局部变量、静态变量
  1. 几种引用类型:强软弱虚,软是gc回收内存不够,弱是直接回收
  1. finalize方法:没有倍GC Roots 引用不一定就会被回收,如果在finalize方法中 又指向了某个GC Roots

016、大厂面试题: JVM中有哪些垃圾回收算法,每个算法各自的优劣

  1. 标记清除法:会制造大量的碎片,造成内存浪费,所以这种直接标记清除掉一个可回收的对象是不可行的
  1. 复制算法:把存活的对象 挨着移到一块,腾出来整块的空闲空间,他避免了碎片,但是还要空出来一块空间才行
  1. 分区: 每次新生代回收大概只有百分之一存活,新生代划为了三部分,其中有10% 的是空着的
    第一次eden区不够用会回收到一个survivor中
    第二次不够用 就把survivor 和Eden 回收到另一个survivor中,如此往复

017、大厂面试题:年轻代和老年代分别适合什么样的垃圾回收算法

  1. 在年轻代躲过15次GC之后进入老年代:控制参数是-XX:MaxTenuringThreshold
  1. 动态年龄判断:
    一批对象的总大小 大于了这块survival区的 50%,那么大于等于这批对象年龄的对象就直接进入老年代
  1. 大对象直接进入老年代:为了防止大对象来回复制,控制参数:-XX:PretenureSizeThreshold
  1. 如果复制清除的过程中 年轻代放不下了 那就要放到老年代了
  1. 老年代空间担保规则

  1. 老年代的回收算法就是 标记-压缩,它的效率比年轻代慢十倍,所以要控制老年代的回收频率

018、 大厂面试题: JVM中都有哪些常见的垃圾回收器,各自的特点是什么

案例

  1. 背景引入
    不停的从sql中取数据并计算,每分钟五百次,分布式系统,每台服务器内分钟100次,每次处理一万条数据 并耗时10s,每台机器 4c 8g,jvm给4g,新生代和老年代各1.5g,
  1. 多久塞满新生代
    一条数据 1kb,每次一万条 10mb,按照8:1:1 的比例分 eden和survior, eden区就是 1.2g ,那么每分钟一百次,那么一分钟之后就快满了
  1. 假设 新生代1分钟后已经有了1GB的对象,需要回收,老年代是1.5g,可以直接回收,存活下来200mb,s区放不下,就直接进入老年代, 像这样运行两次后,老年代还剩1.1g , 最终按照空间担保计算 大概7、8分钟就会有一次FullGC,效率很低
  1. 怎么优化:
    调大新生代的比例,使s区的容量扩大,这样就能使进入老年代的对象尽可能的少
    同时调整参数XX:SurvivorRatio=8,指eden的大小为80%,可以降低这个比例
  1. 垃圾收集器

019、"Stop the World"问题分析: JVM最让人无奈的痛点

  1. 垃圾回收的过程中不能再创建新的对象

  1. STW:
    新生代停顿可能有几十毫秒,老年代的full gc可能长达几十秒, 所以要尽量降低gc 频率,而且像新生代使用多个线程并行回收 能降低回收时间,也是一个办法,jvm本身的迭代也是在不断降低垃圾回收对系统的影响

020、第3周作业:自己动手画出各种垃圾回收算法和垃圾回收器的原理图

021、第3周答疑:本周问题答疑,上周作业点评

  1. cms+parnnew 怎么保证不ygc
    加分代年龄,新生:老年 = 2:1、e:s = 6:2

  1. 什么时候在Minor GC之前就会提前触发一次Full GC?
    新生代现有存活对象>老年代剩余内存情况下,未设置空间担保 或 空间担保失败

第四周

022、一步一图:深入揭秘JVM的年轻代垃圾回收器ParNew是如何工作的

  1. ParNew:
    多个线程同时回收,充分利用cpu资源,加快回收速度,降低stw时间
  1. 线程数量是多少:
    就是核数,也可以通过 -XX:ParallelGCThreads来控制,一般不动它
  1. 服务端模式客户端模式:
    参数: -server -client
    例如:linux 4c 8g的服务端,运行在windows上的长须 客户端
    区别:主要看有多少核,核少的话还多和清理只会加重线程切换负担

023、一步一图:那JVM老年代垃圾回收器CMS工作时,内部又干了些啥

  1. 采用的算法:标记清理,会
  1. CMS回收垃圾有四个步骤:
    初始标记:有stw 标记出gc roots直接引用的对象
    并发标记:无stw,最耗时间,所有对象是否引用和gc roos
    重新标记:  有stw,把并发标记运行期间产生的也给标记上
    并发清理  :无stw,清理掉被标记为垃圾的对象

024、动手实验:线上部署系统时,如何设置垃圾回收相关参数

  1. CMS占用的CPU资源:
    (CPU核数 + 3)/ 4  ,两核的话就相当于占用1c , 4核3c
  1. 浮动垃圾:并发清理阶段只是清理了之前标记好的,此时还会有对象进入老年代,然后短时间内有没有引用了
  1. Concurrent Mode Failure问题
    -XX:CMSInitiatingOccupancyFaction 占用了多少 会触发cms,jdk1.6是92%,剩下的8%就是留给浮动垃圾的,如果浮动垃圾放不下了,或者是说cms回收期间要放入的老年代大于可用空间了
    这个时候就会使用 serial old 垃圾回收器代替cms,进行stw回收
  1. 内存碎片问题:内存碎片过多 会使对象进入老年代找不到连续的空间,进而触发full gc
  1. cms涉及到的其它参数:
    -XX:+UseCMSCompactAtFullCollection :full gc后停止工作线程,整理碎片,默认打开
    -XX:CMSFullGCsBeforeCompaction :多少次full gc整理一次碎片,默认值是0
  1. Full gc为什么会比ygc慢:
    因为 年轻代能活下来的很少,需要追踪的对象较少

025、案例实战:每日上亿请求量的电商系统,年轻代垃圾回收参数如何优化

  1. 背景:
    电商-订单,500w日活,每个浏览20次,10% 50w人下单,算进4小时高峰期,平均每秒几个订单,但如果是在大促场景,每秒钟会有上千订单,如果分三台机器,每台4c8g,每台抗300就差不多了,当时还是要做好优化。
  1. 使用内存估算:
    一个订单1kb,一秒300个,300kb, 放大10~20倍,等于60mb 每秒钟产生的垃圾
  1. 内存怎么分:
    给jvm 4g, 堆3g,新生代老年代各1.5g,  虚拟机栈给1m,  永久代256m,其中的-XX:HandlePromotionFailure”,1.6版本之后无需设置,
“-Xms3072M -Xmx3072M -Xmn1536M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M

  1. s区不够:
    假设每次会有 100mb对象存活进入s区,考虑到动态年龄判断还是可能会有对象进入到老年区,当考虑到系统中的对象都应该是短年龄的,所以要调大新生代,给他2gb的空间,eden区1.6gb,s区200mb, 老年代1gb
  1. 年轻代多大岁数进入老年代:
    -XX:MaxTenuringThreshold没必要调高这个值,一般年龄大的对象如系统中的一些sping组件,他们一般加起来也就几十兆,所以可以是调低这个值,
  1. 最终参数(指定最大值会进入老年代的 垃圾回收器):
“-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -
XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5 -XX:PretenureSizeThreshold=1M -XX:+UseParNewGC -
XX:+UseConcMarkSweepGC”

026、案例实战:每日请求上亿的电商系统,老年代的垃圾回收参数又该如何优化呢

  1. 推算出大概多久 能装满老年代 触发full gc

027、第4周作业:看看你们的线上系统是怎么设置的JVM垃圾回收参数?设置的合理吗

028、第4周答疑:本周问题统一答疑

  1. 为什么老年代垃圾回收比年轻代慢那么多
    年轻代存活少,从gcrtoots 标记存活对象,然后复制到另一边,其余直接清除
    老年代存活对象多,gc roots标记时间就很长,最后也得移动存活对象,防止碎片
  1. 为什么非要有两个s区?
    因为 eden 区和另一个s 区清理了 没地方放

第五周

029、大厂面试题:最新的G1垃圾回收器的工作原理,你能聊聊吗

  1. G1 要解决什么问题
    parnew +cms 的stw 问题
  1. G1垃圾回收器:
    同时回收 老年代和新生代
    将堆分为多个大小相等的region(区域)
    在逻辑概念上 他依然具有新生代老年代, 就是老年代占有哪些区域,新生代占有哪些区域
    最大的特点:设置一个垃圾回收的预期停顿时间
  1. G1如何做到的对垃圾回收导致的系统停顿可控?
    回收价值:看对每个区域回收需要多久能回收多少
    总而言之就是: 在给定的时间范围内,尽可能回收更多的对象
  1. 一个region 即可能存放的是老年代对象  也能是存放年轻代对象,由G1控制,完全动态

030、G1分代回收原理深度图解:为什么回收性能比传统GC更好

  1. 有多少个region 每个region多大?
    JVM 最多可以有2048个region,region 的值必须为2的倍数,每个region的大小就是  堆大小/2048, 一般保持这个默认的设置,也可以通过-XX:G1HeapRegionSize,来进行指定
  1. 其它重要参数:
    -XX:G1NewSizePercent 新生代的region占比,默认是5%,运行中会自动增加这个值
    XX:G1MaxNewSizePercent 新生代的region 最大占比,默认应该是60%
  1. 新生代的 Eden 和Survivor 概念:之前的新生代参数在这里仍然适用-XX:SurvivorRatio=8,如果有100个新生代region,那么就会有80个eden,10个是survivor。
  1. G1的新生代垃圾回收:
    eden对应的区域 不断对象,eden对应的区域也在增多,新生代逐渐扩大,直至占据堆内存的60%,eden也放满了,此时就会触发GC,把enden的区域中的存活对象 放入S1对应的Region
    与以往不同的是:参数-XX:MaxGCPauseMills,可以控制目标停顿时间,g1收集器将在改时间内 尽可能的回收一部分region(对象)
  1. 对象什么时候进入老年代:
    也是 年龄到了 和动态的年龄判断,一部分对象占用了50%以上
  1. 大对象:
    只要超过了1m 就会放到大对象专门的一个或多个region,而老年代和新生代的回收也会把它一块回收掉

031、动手实验:线上系统部署如果采用G1垃圾回收器,应该如何设置参数

  1. 老年代和新生代 混合垃圾回收:
    -XX:InitiatingHeapOccupancyPercent 默认值是45%,指老年代占据了堆内存的45%的时候 就会触发, 从老年代、新生代、大对象 里面都挑一些region 来进行清理
  1. G1的垃圾回收过程:
    初始标记: 有STW,标记GC roots 能直接引用到的对象
    并发标记:无STW,顺着GC roots继续往下标记 所有能引用到的对象,较为耗时,此阶段jvm会记录哪个对象被创建了,哪个对象失去了引用
    最终标记: 有STW, 对并发标记中修改的对象,进行标记 看哪些还存活
    混合回收:会计算老年代中每个Region中的存活对象数量,存活对象的占比,还有执行垃圾回收的预期性能和效率 , 然后STW 全力以赴 在规定的时间内清理尽量多的 region(包括老年代、新生代、大对象)
  1. 参数:
    -XX:G1MixedGCCountTarget  默认值为8,代表在混合回收阶段分8次回收,这样分成多段时间是为了 不让系统一次停顿的时间太长
    -XX:G1HeapWastePercent 默认值为 5%,其中G1的整体采用复制算法,复制完后如果空闲的region达到了堆的5% 就立刻停止本次回收
    -XX:G1MixedGCLiveThresholdPercent: 默认值是85%,代表回收region时,这个region里面存活的对象不超过85% 才回收
  1. 回收失败时Full GC:

032~033 案例实战:百万级用户的在线教育平台,如何基于G1垃圾回收器优化性能

背景与压力估算

  1. 背景:在线教育,排除掉一些频率不高的行为,只考虑高频行为 上课, 99%的流量可能集中在晚上的两三个小时,上课最多的操作就是互动

  1. 压力估算:
    三小时高峰,60w用户,每个用户1小时使用时间,相当于一小时20w用户,一分钟一次交互,每秒钟3000次的交互操作,需要5台4c 8g 来抗,一台扛600, 估算这一秒600次请求占用 3mb内存

优化点

  1. G1垃圾回收器的默认内存布局 :不用动
  1. GC停顿的时间(目标时间)-XX:MaxGCPauseMills  要通过工具设置
  1. MixedGC 的优化: 混合清理优化的关键还是让 尽量少的对象进入老年代

034、第一 阶段复习: 当你开发完一个系统准备部署 上线时,如何设置JVM参数

学了不练,学了不复习,学了不总结,基本等于白学。希望大家记住这句话。

035、第5周答疑:本周问题答疑,上 周作业点评

第六周

036、糟糕!运行着的线上系统突然卡死无法访问,万恶的JVM GC

  1. 最怕系统卡顿  STW
  1. 年轻代 一般GC 一次对系统影响不大:只要给足够的内存,而且采用的复制算法只需要标记少量的存活对象即可,效率较高
  1. 什么时候年轻代GC 对系统影响大:新生代有几十gb的超大内存,并且访问量很大,gc频率高,可能会卡顿几秒
  1. 如何解决上面这种大内存 机器新生代GC慢的问题:
    用G1, 它可以在给定的时间内 回收一部分内存
  1. 频繁触发的老年代GC的问题:
    进入老年代的三个条件:  年龄太大、动态年龄(几个年龄 的对象加起来超过了s区的50%)、s区放不下
    老年代回收要比新生代慢10倍
  1. JVM的性能优化到底在优化什么
    就是因为内存分配参数设置不合理,导致你的对象频繁的进入老年代,然后频繁触发老年代gc,导致系统每隔几分钟就要卡死几秒钟。

037、大厂面试题:解释一下什么是Young GC和Full GC

  1. Minor GC / Young GC : 年轻代的回收
  1. Full GC/Old GC :  前者是 老年代满了触发的gc,Full GC会回收所有内存空间 老年代、新生代、永久代,后者就是老年代GC。
  1. Mojor GC:不常用,需要问对方到底指的是 old gc 还是 full gc
  1. MixedGC:G1收集器中 老年代占比达到45%收集

038、厂面试题: Young GC和Full GC分别在什么情况下会发生

  1. 永久代满了怎么办:也会触发full gc

039、案例实战:每秒10万并发的BI系统是如何频繁发生Young GC的

  1. 什么是BI(Business Intelligence ): 给商家提供一些报表数据

  1. 刚上线时候的系统架构 -> 实时报表 用户增多 定时请求  -> 增大机器配置
    扩大机器内存后 对于大堆的复制算法 仍然可能会停顿1s左右,这时候可以采用G1 垃圾回收器

040、案例实战:每日百亿数据量的实时分析引擎,为啥频繁发生Full GC

  1. 背景: 数据规模上亿,从MySql中读取大量数据 到内存中进行计算,每台机器每分钟 100次,一次10秒,10w数据,4c8g,jvm 4g,  新生老年 各1.5g
  1. 问题分析:经计算 1分钟塞满eden,3分钟触发full gc
  1. 解决:调大新生代内存空间 减少full gc频率
  1. 系统的工作负载扩大十倍: 一分钟两三次Full GC,  解决 机器内存生成32gb,  而且不用换成g1,因为他对停顿时间没有要求

041、第6周作业:打开脑洞!如果你的线上系统压力增长100倍,会有频繁GC问题吗

肯定会有 会挂掉

042、第6周答疑:本周问题答疑汇总

  1. g1 为什么适合大内存:
    一句话总结:如果使用 pn+cms  回一次回收几十gb的垃圾,停顿时间会很长
  1. 动态年龄:年龄1+年龄2+年龄3的对象占据了超过50%的Survivor,就会让年龄3以上的对象进入老年代,动态年龄判定规则应该是这样的

第七周

043、动手实验:自己动手模拟出频繁Young GC的场景体验一 下

  1. 使用参数:

-XX:NewSize=5242880 -XX:MaxNewSize=5242880 -XX:InitialHeapSize=10485760 -
XX:MaxHeapSize=10485760 -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=10485760 -
XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -
Xloggc:gc.log

  1. 实例代码
        byte[] array1 = new byte[1024 * 1024];array1 = new byte[1024 * 1024];array1 = new byte[1024 * 1024];array1 = null;byte[] array2 = new byte[2 * 1024 * 1024];

  1. 查看日志

044、高级工程师的硬核技能: JVM的Young GC日志应该怎么看

  1. 对日志进行分析

045~046、动手实验:自己动手模拟出对象进入老年代的场景体验一下

  1. 通过代码 模拟出对象进入老年代,比如触发动态年龄
  1. 一次清理,如果s区放不下 就要放到老年代,但不是所有的对象都放到老年代,会有一部分留下来去到另一个s区。

047、高级工程师的硬核技能: JVM的Full GC日志应该怎么看

  1. 代码模拟,日志分析

048、第7周作业:自己尝试着分析一把你们线上系统的JVM GC日志

049、第7周答疑:本周问题答疑汇总

第八周

050、动手实验:使用jstat摸清线上系统的JVM运行状况

需要知道的命令

  1. ps -aux | grep java : 找出java 进程的pid(linux),  windows 可以直接 tasklist
  1. jstat -gc PID  (linux)
S0C:这是From Survivor区的大小
S1C:这是To Survivor区的大小
S0U:这是From Survivor区当前使用的内存大小
S1U:这是To Survivor区当前使用的内存大小
EC:这是Eden区的大小
EU:这是Eden区当前使用的内存大小
OC:这是老年代的大小
OU:这是老年代当前使用的内存大小
MC:这是方法区(永久代、元数据区)的大小MU:这是方法区(永久代、元数据区)的当前使用的内存大小
YGC:这是系统运行迄今为止的Young GC次数
YGCT:这是Young GC的耗时
FGC:这是系统运行迄今为止的Full GC次数
FGCT:这是Full GC的耗时
GCT:这是所有GC的总耗时


其它命令

jstat -gccapacity PID:堆内存分析
jstat -gcnew PID:年轻代GC分析,这里的TT和MTT可以看到对象在年轻代存活的年龄和存活的最大年龄
jstat -gcnewcapacity PID:年轻代内存分析
jstat -gcold PID:老年代GC分析
jstat -gcoldcapacity PID:老年代内存分析
jstat -gcmetacapacity PID:元数据区内存分析

通过结果计算重要信息

  1. 新生代对象的增长速度:
    jstat -gc PID 1000 10: 每隔一秒 统计十次,可以看出来 eden区在这一时间内增长了多少
  1. ygc的触发频率 和每次的耗时:
    触发频率可以通过增长速率,和内存容量判断。
    每次耗时: 总耗时/次数即可
  1. 每次ygc 有多少对象存活,有多少进入了老年代:先看大概每隔多久 执行了一次ygc  ,比如三分钟,然后观察增长了多少
  1. Full GC的触发时机和耗时: 通过老年代的增长速率计算,耗时也可通过full 的总耗时和次数算得

051、动手实验:使用jmap和jhat摸清线上系统的对象分布

jmap

  1. jmap: 可以用来了解对象分布
  1. jmap -histo PID

  1. 生成当前对象的 快照文件jmap -dump:live,format=b,file=dump.hprof PID 然后使用命令 jhat dump.hprof -port 7000  即可使用jhat 分析,它内置web服务器,可以图形化

052、从测试到上线:如何分析JVM运行状况及合理优化

  1. 开发好之后的 系统性能预估,让尽量少的对象进入到老年代
  1. 系统压测阶段:在此阶段也要关注好jvm的运行状态
  1. 对线上系统进行jvm监控:在高峰、低峰时段使用 jstat、jmap、jhat等工具去查看,或者使用一些监控系统Zabbix、OpenFalcon、Ganglia  进行查看

053、案例实战:每秒10万并发的BI系统,如何定位和解决频繁Young GC问题

  1. 技术痛点:自动刷新报表+大量数据报表
  1. 使用代码模拟
// 参数
-XX:NewSize=104857600 -XX:MaxNewSize=104857600 -XX:InitialHeapSize=209715200
-XX:MaxHeapSize=209715200 -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15-XX:PretenureSizeThreshold=3145728 -XX:+UseParNewGC-XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps-Xloggc:gc.log
// 代码
public class Demo1 {public static void main(String[] args) throws Exception {Thread.sleep(30000);while (true) {loadData();}}private static void loadData() throws Exception {byte[] data = null;for (int i = 0; i < 50; i++) {data = new byte[100 * 1024];}data = null;Thread.sleep(1000);}
}

  1. 然后用 jstat -gc 51464 1000 1000  ,查看日志,得出各类信息

054、案例实战:每日百亿数据量的实时分析引擎,如何定位和解决频繁Full GC问

  1. 模拟程序:

  1. 查看日志  分析内存
  1. 发现问题: 年轻代GC也很慢,这是因为s区放不下,老年区也放不下 还得等它full gc完毕
  1. 怎么优化: 调大堆内存,调大年轻代内存,减少full gc,排除它的干扰

055、第8周作业

动手实战 查看一下运行状态

056、第8周答疑:本周问题答疑汇总

  1. 为了避免full gc:使每次minor gc后,存活的对象尽量能放在s区,不要放到老年代

第九周

057、案例实战:每秒十万QPS的社交APP如何优化GC性能提升3倍

  1. 背景: 社交系统,流量最大的一个模块是 查看一个用户的信息, 查看一次5M,高峰期 10w+qps
  1. 系统出现的问题:请求量过大,年轻代放不下 存活对象多,要频繁放到老年代 触发gc
    内存碎片:-XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=5, 使用cms 老年代收集器,其中5次full gc才会整理一次碎片
  1. 老年代频繁gc 加大内存就行了,内存碎片 可以设置 CMSFullGCsBeforeCompaction = 0,每次full gc之后都整理一下碎片

058、案例实战:垂直电商APP后台系统,如何对Full GC进行深度优化

  1. 垂直电商:在一些细分的领域,如服装定制,分期消费,日活几十万
  1. 定义一个专门的 JVM参数模板,机器内存8g 给年轻代4g, s区大概分到300m,尽量减少Full GC,变成几天一次,并且加入 Compaction 确保每次full  gc之后都会执行一次压缩 来解决内存碎片问题
  1. cms回收器 优化的其它参数:
    -XX:+CMSParallelInitialMarkEnabled  : 代表cms 初始标记阶段使用多个线程
    -XX:+CMSScavengeBeforeRemark: 在cms 重新标记之前 先做一次ygc,  尽量减少耗时
  1. 最终结果:几分钟 或十几分钟 一次ygc,每次耗时几十毫秒,full gc几天一次,每次几百毫秒

059、案例实战:新手工程师不合理设置JVM参数,是如何导致频繁Full GC的

  1. 背景: 一个工程师学了半吊子 随便搞了个参数 然后频繁触发full gc
  1. 查看日志 发现问题: MetaData 内存占用出现波动,存在类不停的被加载,导致反复Full GC,启动参数添加
    -XX:TraceClassLoading -XX:TraceClassUnloading, 发现有个类不断被加载,而Class 对象是软引用
Method method = XXX.class.getDeclaredMethod(xx,xx);
method.invoke(target,params);


而正常情况下 是不会频繁导致gc的

  1. 解决问题:
    软引用什么时候 被回收:clock - timestamp <= freespace * SoftRefLRUPolicyMSPerMB,
    “clock - timestamp”代表了一个软引用对象他有多久没被访问过了,freespace代表JVM中的空闲内存空
    间,SoftRefLRUPolicyMSPerMB代表每一MB空闲内存空间可以允许SoftReference对象存活多久。
    而这里 SoftRefLRUPolicyMSPerMB 被设置成了0,这就导致需要不断的gc 回收掉刚刚创建的软引用,而正常情况下应该设置个小几秒。

060、案例实战: 一次线上系统每天数十次Full GC导致频繁卡死的优化实战

  1. 未优化前的 系统情况
机器配置:2核4G
JVM堆内存大小:2G
系统运行时间:6天
系统运行6天内发生的Full GC次数和耗时:250次,70多秒
系统运行6天内发生的Young GC次数和耗时:2.6万次,1400秒

  1. 发现问题 是大对象!
  1. 怎么解决:一是解决代码上的bug,不要从数据库里面查出来那么多数据 搞出来那么多对象
    而是调大新生代,S区,调整参数为 -XX:CMSInitiatingOccupancyFraction=92,让老年代占比达到92%才触发回收

061、案例实战:电商大促活动下,严重Full GC导致系统直接卡死的优化实战

  1. 问题: 发现每秒都在gc,然后看到有人用了  System.gc(),这样做就是 命令jvm 频繁去 Full GC
  1. 怎么解决:使用 -XX:+DisableExplicitGC , 不允许代码 显示触发GC

062、第9周作业

复习题,自己总结本周生产案例的问题定位、原因分析以及解决思路!

063、第9周答疑以及学员思考题总结汇总

可以

第十周

064、案例实战: 一次线上大促营销活动导致的内存泄漏和Full GC优化

  1. 背景:活动促销 吸引了大量用户,流量暴增,CPU负载飙升
  1. CPU负载升高的两个可能的原因:
    一是 系统本身创建了很多线程
    二是 Jvm在频繁的进行垃圾回收,这个过程也非常消耗CPU资源
  1. 发现问题:频繁Full GC
频繁Full GC的问题,一般可能性有三个:
内存分配不合理,导致对象频繁进入老年代,进而引发频繁的Full GC;
存在内存泄漏等问题,就是内存里驻留了大量的对象塞满了老年代,导致稍微有一些对象进入老年代就会引发Full GC;
永久代里的类太多,触发了Full GC

  1. 如何解决:
    使用强大的内存分析工具 MAT进行分析
    先获得线上的快照 jmap -dump:format=b,file=文件名 [服务进程ID]
    然后进行分析,发现系统创建了大量对象(无LRU的缓存系统),并且回收不掉,发生了内存泄露

065、案例实战:百万级数据误处理导致的频繁Full GC问题优化

  1. 背景:收到了大量用户反馈 和报警
  1. 发现问题:堆内存20g 老年代 新生代各10g,eden区一分钟塞满,然后进入老年代几个g,Full gc一次就要暂停10秒钟
  1. 怎么解决: 简单调整参数,扩大s区也没用,因为一次就几个g,所以要从代码优化避免那么多数据加载到内存,使用Mat 分析内存使用, 然后发现 几十万条数据使用了 String.split(),创建了很多小数组,然后要把这个给优化掉

066、阶段性复习: JVM运行原理和GC原理你真的搞懂了吗

  1. 正常情况下的系统:几分钟或几十分钟一次ygc,一次耗时几毫秒 到几十毫秒, fgc 几分钟一次 几小时一次,一次几百毫秒都正常

067、阶段性复习: JVM性能优化到底该怎么做

  1. 需要多看 复习!

068、如何为你的面试准备自己负责的系统中的JVM优化案例

  1. 可以设想自己的系统 压力增大了10倍 然后顺着答下去
  1. jvm优化注意点:不要自己随便设置一些奇怪的参数

069、关于作业的说明

没了 作业

070、第10周答疑汇总

可以

第十一周

071、Java程序员的梦魇:线上系统突然挂掉,可怕的OOM内存溢出

  1. 有限的内存放不下了

072、大厂面试题:什么是内存溢出?在哪些区域会发生内存溢出

metaSpace 、java 虚拟机栈、堆内存

073、Metaspace区域是如何因为类太多而发生内存溢出的

  1. 无法回收  堆积了越来越多的类,设置MetaSpace大小的参数为: -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m
  1. 什么情况会导致它溢出:
    第一种原因是 使用了默认的参数,分配空间过小
    第二种原因是 使用反射,生成了过多的类

074、无限制的调用方法是如何让线程的栈内存溢出的

递归,要防止无限次调用,然后导致sof

075、对象太多了!堆内存实在是放不下,只能内存溢出

对象太多 都是活的放不下

076、动手实验:自己模拟出JVM Metaspace内存溢出的场景体验一下

用代码模拟,使用cjlib动态生成大量类

077、动手实验:自己模拟出JVM栈内存溢出的场景体验一下

  1. 一台  4c8g的机器,512给Metaspace,4g给堆内存,然后还剩三g内存,其中一两个g 给栈内存,一般tomcat 几百个线程,每个线程占内存给个1mb,而如果分配的太多的话 又会造成线程减少。
  1. 实验: 代码模拟,然后是1m内存 够一个方法递归调用 5000次

078、动手实验:自己模拟出JVM堆内存溢出的场景体验一下、

代码模拟

079、案例实战:一个超大数据量处理系统是如何不堪重负OOM的

中间件故障,在本地丢了大量数据,最终导致OOM

080、案例实战:两个新手工程师误写代码是如何导致OOM的

新手背锅

081、如何对对线上系统的OOM异常进行监控和报警

  1. 接入系统 不要等客服来通知你挂掉了

082、一个关键问题:如何在JVM内存溢出的时候自动dump内存快照

  1. 在OOM即将发生的时候自动dump快照
# 打开该功能
-XX:+HeapDumpOnOutOfMemoryError 
# 存放的位置
-XX:HeapDumpPath=/usr/local/app/oom

  1. 到目前位置比较全的 参数模板
“-Xms4096M -Xmx4096M -Xmn3072M -Xss1M -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -
XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFaction=92 -
XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSParallelInitialMarkEnabled -
XX:+CMSScavengeBeforeRemark -XX:+DisableExplicitGC -XX:+PrintGCDetails -Xloggc:gc.log -
XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/app/oom”

083、动手实验: Metaspace区域内存溢出的时候, 应该如何解决

从内存快照去分析得到底是谁占用了那么多的内存

084、动手实验: JVM栈内存溢出的时候,应该如何解决

直接去看下日志就行了

085、动手实验: JVM堆内存溢出的时候,应该如何解决

看下哪个对象造成的

086、 案例实战:每秒仅仅上百请求的系统为什么会因为OOM而崩溃

  1. 发现问题: 系统每秒只有100+的请求,错误日志为"http-nio-8080-exec-1089" java.lang.OutOfMemoryError: Java heap space 就是tomcat工作线程抛出了 oom
  1. 解决:
    发现工作线程多达 400个,因为每个都需要运行4秒, 运行四秒是因为有个远程调用,所以调小超时时间
    然后是每个线程都创建了 10M的数组,用max-http-header-size 设置的,这里要调小

087、案例实战: Jetty 服务器的NIO机制是如何导致堆外内存溢出的

  1. 使用jetty作为web服务器,然后堆外内存溢出了,报的异常是nio handle failed java.lang.OutOfMemoryError: Direct buffer memory
  1. 发现问题: 堆外内存迟迟无法被释放掉,然后有好多 DirectByteBuffer 对象(关联了大量堆外内存) 进入了老年代,而老年代设置的很大,迟迟没法gc,directByteBuffer 对象没法被及时回收,占用的直接内存也没法被即使回收
  1. 解决:
    NIO 是考虑到这个问题的,所以每次分配堆外内存的时候 会使用System.gc(), 但是系统禁止了代码gc 可以打开。
    还有就是合理分配内存要。

088、案例实战:一次微服务架构下的RPC调用引发的OOM故障排查实践

  1. 背景  A远程调用B,B发生了 OOM,导致宕机,重启后一段时间还会有这种情况
  1. 发现问题:在rpc框架中有东西创建了大量的 byte[] 数组,然后分析源码进行追踪,发现是B服务将请求数据 反序列化时创建了很多字节数组,其中传输的数据可以看成 一个request对象, B服务发现这个对象有问题 反序列化失败 就创建了大数组 将流放了进去
  1. 解决 把那个数组调小,参数保持一致

089、案例实战: 一次没有WHERE条件的SQL语句引发的OOM问题排查实践

  1. 背景 :没加条件,一次性查询出来了 上百万条数据
  1. 其它:这是一个经典的使用 MAT工具的教程

090、本周思考题

分析快照

091、本周答疑问题汇总

可以

第十二周

092、案例实战:每天10亿数据的日志分析系统的OOM问题排查实践.pdf

  1. 背景:Kafka 不停的获取数据,然后对数据进行清洗
  1. 发现问题:堆内存OOM,MAT分析快照 发现方法递归调用创建了大量的 char[] 数组, 而堆内存也过小 总量只有1gb,每秒一次full gc,每次回收的并不多,使用jstat 查看发现老年代会出现容量猛增的情况
  1. 解决问题:增大堆内存,优化代码

093、案例实战: 一次服务类加载器过多引发的OOM问题排查实践

  1. 背景: 发现系统假死,接口无法供别人调用
  1. 发现问题:使用top命令,发现cpu占有率不高,而内存占用达到了50%,占用率高会出现下面三种情况,排查后知道是第三种,然后使用top命令 和jstat命令 观察一段时间,在内存占用率高的时候 导出快照,并分析,发现大量的ClassLoader 加载了大量的数组

  1. 优化代码

094、案例实战:一个数据同步 系统频繁OOM内存溢出的排查实践

  1. 从kafka中拉取数据 做数据同步
  1. 发现问题:直接看到问题的本质:为什么会出现内存溢出  一是瞬间创建了太多,放不下,二是好多对象赖在内存里,gc回收不掉
  1. 本案例的问题:队列中 引用了太多的list 而且无法及时回收

095、总复习:线上系统的JVM参数优化、GC问题定位排查、OOM分析解决

JVM 运行原理、合理的参数、遇到问题如何解决

096、专栏彩蛋:面试中如何展现自己的JVM实战经验

第一个是你们生产环境的系统的JVM参数怎么设置的?为什么要这么设置?
还有一个是你在生产环境中的JVM优化经验可以聊聊?
另外一个是说说你在生产环境解决过的JVM OOM问题?

http://www.lryc.cn/news/587181.html

相关文章:

  • DTU数据处理
  • [spring6: @EnableSpringConfigured]-编译时织入
  • AWS云安全详解:账号管理与最佳安全实践
  • AI Agent开发学习系列 - langchain之Agent智能体(2):几种不同的内置agent类型
  • IPC框架
  • ID生成策略
  • ​[Dify]-基础入门7- 探索 Dify 知识库:打造专属知识大脑
  • 一些git命令
  • 系统设计 --- 双重检查锁定
  • 前端基础知识TypeScript 系列 - 04(TypeScript 中接口的理解)
  • 深度学习图像分类数据集—角膜溃疡识别分类
  • php生成二维码
  • 人工智能之数学基础:神经网络的矩阵参数求导
  • ABP VNext + 多级缓存架构:本地 + Redis + CDN
  • Redis集群方案——哨兵机制
  • 前端工程化-构建打包
  • Java 8 异步编程和非阻塞操作工具 CompletableFuture
  • DVWA CSRF漏洞分析与利用
  • C语言---自定义类型(上)(结构体类型)
  • 更换docker工作目录
  • 4. 关于CEF3 使用的一些记录及仓颉端封装的情况
  • [2025CVPR]DenoiseCP-Net:恶劣天气下基于LiDAR的高效集体感知模型
  • Android事件分发机制完整总结
  • 《Python JSON 数据解析全指南:从基础到实战(含 jsonpath 与 Schema 验证)》
  • 002大模型基础知识
  • Opencv---blobFromImage
  • Llama系列:Llama1, Llama2,Llama3内容概述
  • 互联网大厂Java面试:从Spring Boot到微服务的场景应用
  • RHCIA第二次综合实验:OSPF
  • anaconda常用命令