JVM从入门到入土之实战JVM调优(一)

简介: 前言文本已收录至我的GitHub仓库,欢迎Star:github.com/bin39232820…种一棵树最好的时间是十年前,其次是现在

絮叨


前面的章节

其实前面的都只是铺垫,真正的东西来了,是骡子是马总得遛遛才知道,所以呢,大家一起来看看针对不同的业务场景,分析具体的业务来调优


每日上亿请求的电商系统JVM调优


背景引入

我们的背景是电商系统,电商系统其实一般会拆分成为很多的子系统独立部署,比如商品系统,订单系统,活动系统,数据统计系统,会员系统等。

这边就以订单系统来做例子

每日上亿请求,那么它会有多少活跃用户

一般每个用户平均访问20次,那么上亿大概有500W 日活跃

那么继续推算 500W 日活 能有多少下单呢?

如果是10% 大概会有50W人下单,那么每天大概是50W订单

如果50W订单集中在4小时的高峰期内,那么平均每秒也才几十个订单,如果是几十个订单其实并不要过多去关注JVM,基本上每秒占用一些新生代得到内存,隔很久,新生代才会满,然后一次Minor GC 内存就出来了,没有啥压力

但是秒杀,大促销的场景就不这样了

如果是双11 基本上几分钟就会有几十万的订单,那么每秒的下单量 可能是上千 破万


抗住大促销需要几台机器

基本上3台机器可以了  每个机器每秒300个下单 部署的是4核8G

从本身来说这个硬件资源是够了 扛住每秒300个 问题不大

但是问题在于需要对JVM 有限的内存资源合理分配,让JVM GC次数减少,而且尽量避免Full GC


大促销订单系统的内存使用模型

基本上每秒处理300个下单来估算,其实因为下单涉及的接口请求是比较耗时的,所以呢每秒处理100到300是差不多的

那么每个订单的实体我的字段我按照1kb来算,单单300个订单就会有300kb的内存开销

然后算上一系列的其他业务对象 就是放大10倍到20倍

此外,除了下单外 还有很多跟订单相关的其他操作,比如查询这些 我们再扩大10倍

那么每秒就是 300kb * 20 * 10=60MB的内存开销,这个内存开销会再1秒后可以是回收状态,如下图


内存如何分配


假设我们是4核8G的机器,那么给到JVM 一般是4G,剩下的给操作系统来使用,其中堆内存我们可以给到3G,新生代给1.5G,老年代给1.5G

然后每个线程的Java虚拟机栈是1M,那么JVM如果有几百个线程大概是几百M

然后永生代 给256内存,基本上4G差不多了


此时JVM的示意图如下

接下来就很明确了 订单系统每秒大概300个订单 ,都是占据新生代60MB的内存,那么新生代大概是25秒中就会占满,如下图


25秒之后就会要进行Minor GC了,此时因为有-XX:HandlePromotionFailure选项,所以你可以认为需要进行的检查主要是比较 老年代可用空间大小和历代Minor GC后进入老年代对象的平均大小,刚开始肯定这个检查是可以通过的

所以Minor GC直接运行,一下子可以回收掉99%的新生代对象,因为除了 最近一秒的订单请求还在处理,大部分的订单早就处理完毕了,所以此时可能存活的对象也就是100MB


但是问题来了 如果-XX:SuruvivorRatio 参数默认为8,那么此时Eden大概占用1.2GB内存,每个Surviuvor是150MB,如下图


然后再次运行20秒,把Eden区暂满,再次垃圾回收Eden和S1中的对象,存活对象可能还是100MB左右 会进入S2,如下图



此时JVM参数如下


新生代垃圾回收优化之一:Survivor空间够不够

首先在进行JVM优化的时候,第一个要考虑的问题就是你通过估算,你的新生代的Survivor到底够不够

按照上面的逻辑 首先每次新生代垃圾回收是100MB ,有可能会突破150MB,那么岂不是经常出现Minor GC过后无法放到Survivor中,那么岂不是会频繁进入到老年代?

还有,即使是Minor GC后 的对象少于150MB,但是即使是100MB的对象进入了Survuvor区,因为这是一批相同年龄对象,直接超过了空间的50%,此时也可能会导致对象进入老年代


所以按这个模型来说,Surivor区域的空间明显是不够的

其实这里建议是把新生代调整为2G 老年代1G,那么此时Eden为1.6G,每个Survivor为200MB,如下图

这个时候,Survivor区域变大,就大大降低了新生代GC过后存活对象在Survivor里放下不下的问题,或者同龄对象超过50%的情况

这样就大大降低了 新生代进入老年代的概率

此时JVM的参数如下


其实对于任何一个系统,首先类似上面的内存使用模型预估以及合理的分配内存,尽量让每次Minor GC后的对象都留着Survivor里,不要进入老年代,这个是你首先要优化的地方


新生代对象躲过多少次垃圾回收进入老年代

大家知道 一个对象连续躲过15次垃圾回收会自动的进入老年代

其实按照上面的内存运行模型来说,基本上20秒触发一次Minor GC ,也就是一个对象再新生代几分钟还没回收,它就会进入老年代

有写博客说,要把参数设置高一点 20 30 ,其实这种说法是不对的

因为你对这个参数考虑必须结合系统的运行模型来说,如果躲过了15次GC 都几分钟,如果一个对象几分钟都不回收,肯定是系统里@Service这类注解

所以谁,考虑问题,一定不要人云亦云,要结合运行原理,自己推演和思考,不同的业务系统还都是不一样的

其实这个参数你也可以适当的降低它的值,比如说降到5次,也就是一个对象能够躲过5次Minor GC,在新生代超过1分钟,尽快让他进入老年代,别再新生代里面占着内存了

总之,这个参数必是结合你的系统具体运行模型来考虑的,此时JVM参数如下


多大的对象直接进入老年代?

另外一个逻辑就是说,大对象可以直接进入老年代,因为大对象说明是要长期存活和使用的

比如在JVM里可能会缓存一些数据,这个一般可以结合自己系统中到底有没有创建大对象来决定的

但是一般来说,这个给他设置1MB足够,因为很少有对象超过1MB的大对象,如果有,可能是你提前分配的,大List之类的

此时JVM的参数如下


指定垃圾回收器

同时大家别忘记了要指定垃圾回收器,新生代使用ParNew,老年代使用CMS,如下JVM参数:


总结


其实大家看完这个案例,就可以直接去设置自己系统的JVM的参数了,看看你新生代的大小,老年代的大小,Eden和Survivor的大小,然后午估算一下你的系统运行模型

  • 每秒占用多少内存?
  • 多长时间触发一次Minor GC
  • 一般Minor GC 后还有多少存活对象
  • Survivor能放的下吗
  • 会不会频繁因为Survivor放不下导致对象进入老年代?
  • 会不会因为动态年龄判断规则进入老年代?


结尾


JVM实战一,这个是最基本的调优,所有的JVM调优都是根据业务估算来的

因为博主也是一个开发萌新 我也是一边学一边写 我有个目标就是一周 二到三篇 希望能坚持个一年吧 希望各位大佬多提意见,让我多学习,一起进步。

相关文章
|
12天前
|
Arthas 监控 Java
JVM进阶调优系列(9)大厂面试官:内存溢出几种?能否现场演示一下?| 面试就那点事
本文介绍了JVM内存溢出(OOM)的四种类型:堆内存、栈内存、元数据区和直接内存溢出。每种类型通过示例代码演示了如何触发OOM,并分析了其原因。文章还提供了如何使用JVM命令工具(如jmap、jhat、GCeasy、Arthas等)分析和定位内存溢出问题的方法。最后,强调了合理设置JVM参数和及时回收内存的重要性。
|
10天前
|
监控 Java 编译器
Java虚拟机调优实战指南####
本文深入探讨了Java虚拟机(JVM)的调优策略,旨在帮助开发者和系统管理员通过具体、实用的技巧提升Java应用的性能与稳定性。不同于传统摘要的概括性描述,本文摘要将直接列出五大核心调优要点,为读者提供快速预览: 1. **初始堆内存设置**:合理配置-Xms和-Xmx参数,避免频繁的内存分配与回收。 2. **垃圾收集器选择**:根据应用特性选择合适的GC策略,如G1 GC、ZGC等。 3. **线程优化**:调整线程栈大小及并发线程数,平衡资源利用率与响应速度。 4. **JIT编译器优化**:利用-XX:CompileThreshold等参数优化即时编译性能。 5. **监控与诊断工
|
21天前
|
存储 监控 Java
JVM进阶调优系列(8)如何手把手,逐行教她看懂GC日志?| IT男的专属浪漫
本文介绍了如何通过JVM参数打印GC日志,并通过示例代码展示了频繁YGC和FGC的场景。文章首先讲解了常见的GC日志参数,如`-XX:+PrintGCDetails`、`-XX:+PrintGCDateStamps`等,然后通过具体的JVM参数和代码示例,模拟了不同内存分配情况下的GC行为。最后,详细解析了GC日志的内容,帮助读者理解GC的执行过程和GC处理机制。
|
29天前
|
Arthas 监控 数据可视化
JVM进阶调优系列(7)JVM调优监控必备命令、工具集合|实用干货
本文介绍了JVM调优监控命令及其应用,包括JDK自带工具如jps、jinfo、jstat、jstack、jmap、jhat等,以及第三方工具如Arthas、GCeasy、MAT、GCViewer等。通过这些工具,可以有效监控和优化JVM性能,解决内存泄漏、线程死锁等问题,提高系统稳定性。文章还提供了详细的命令示例和应用场景,帮助读者更好地理解和使用这些工具。
|
1月前
|
监控 架构师 Java
JVM进阶调优系列(6)一文详解JVM参数与大厂实战调优模板推荐
本文详述了JVM参数的分类及使用方法,包括标准参数、非标准参数和不稳定参数的定义及其应用场景。特别介绍了JVM调优中的关键参数,如堆内存、垃圾回收器和GC日志等配置,并提供了大厂生产环境中常用的调优模板,帮助开发者优化Java应用程序的性能。
|
1月前
|
存储 监控 算法
JVM调优深度剖析:内存模型、垃圾收集、工具与实战
【10月更文挑战第9天】在Java开发领域,Java虚拟机(JVM)的性能调优是构建高性能、高并发系统不可或缺的一部分。作为一名资深架构师,深入理解JVM的内存模型、垃圾收集机制、调优工具及其实现原理,对于提升系统的整体性能和稳定性至关重要。本文将深入探讨这些内容,并提供针对单机几十万并发系统的JVM调优策略和Java代码示例。
51 2
|
1月前
|
Arthas 监控 Java
JVM知识体系学习七:了解JVM常用命令行参数、GC日志详解、调优三大方面(JVM规划和预调优、优化JVM环境、JVM运行出现的各种问题)、Arthas
这篇文章全面介绍了JVM的命令行参数、GC日志分析以及性能调优的各个方面,包括监控工具使用和实际案例分析。
46 3
|
1月前
|
算法 Java
JVM进阶调优系列(4)年轻代和老年代采用什么GC算法回收?
本文详细介绍了JVM中的GC算法,包括年轻代的复制算法和老年代的标记-整理算法。复制算法适用于年轻代,因其高效且能避免内存碎片;标记-整理算法则用于老年代,虽然效率较低,但能有效解决内存碎片问题。文章还解释了这两种算法的具体过程及其优缺点,并简要提及了其他GC算法。
 JVM进阶调优系列(4)年轻代和老年代采用什么GC算法回收?
|
1月前
|
Java
JVM进阶调优系列(5)CMS回收器通俗演义一文讲透FullGC
本文介绍了JVM中CMS垃圾回收器对Full GC的优化,包括Stop the world的影响、Full GC触发条件、GC过程的四个阶段(初始标记、并发标记、重新标记、并发清理)及并发清理期间的Concurrent mode failure处理,并简述了GC roots的概念及其在GC中的作用。
|
1月前
|
算法 Java
JVM进阶调优系列(3)堆内存的对象什么时候被回收?
堆对象的生命周期是咋样的?什么时候被回收,回收前又如何流转?具体又是被如何回收?今天重点讲对象GC,看完这篇就全都明白了。

相关实验场景

更多