jvm 老年代和新生代比例多少合适_JVM调优实战之预估调优

本文通过扁鹊三兄弟的故事引出JVM调优的三个阶段,重点讨论预估调优,即在项目部署前基于并发量调整堆区设置以防止OOM。以亿级流量秒杀系统为例,计算每秒在Eden区生成的对象大小,进而推算出新生代与老年代的合理比例,避免频繁Full GC导致的性能问题。调优涉及的知识点包括Java数据类型、对象大小计算、JVM内存模型等。
摘要由CSDN通过智能技术生成

不知道大家有没有听过扁鹊三兄弟的故事,故事是这样的:魏文王问扁鹊:你们三兄弟都精通医术,谁是医术最好的呢?扁鹊回答:大哥最好,二哥次之,我最差。魏文王不解的问:为什么这样说呢?扁鹊答:大哥治病是在病人发作之前,那时候病人自己不觉得有病,但大哥就下药铲除了病根,使他的医术难以被人认可,所以没有名气;二哥治病是在病起之初,症状尚不十分明显,病人也没有觉得痛苦,二哥就能药到病除,所以大家的印象就是小病找二哥;我治病是在病人危急时刻,病人痛苦万分,家人心急如焚,他们看到我治病时在经脉上扎针穿刺,或以毒试毒,或动大手续使病人减轻痛苦直至痊愈,所以我闻名天下。魏文王大悟。

为什么要讲这个故事呢?

因为JVM调优跟这个故事很像。JVM调优也有这三个阶段:

1、在项目部署到线上之前,基于可能的并发量进行预估调优

2、在项目运行过程中,部署监控收集性能数据,平时分析日志进行调优

3、线上出现OOM,进行问题排查与调优

这里每一点展开来讲涉及到的知识点都比较多,打算用三篇文章讲清楚每一阶段的调优,本篇文章讲第一阶段,主要讨论堆区调优。

JVM调优

很多同学可能不太理解:为什么要做JVM调优?

一、防止出现OOM

即在系统部署之前,根据一些关键数据进行预估不同内存区域需要给多少内存合适

二、解决OOM

即线上出现了OOM,应该如何调优以保证程序能正常运行

二、减少full gc出现的频率

这个主要是堆区,如果设置的不合理就会频繁full gc,导致系统运行一阵暂停一阵,导致体验下降

场景

这里以亿级流量秒杀电商系统为例:

  1. 如果每个用户平均访问20个商品详情页,那访客数约等于500w(一亿 / 20)

  2. 如果按转化率10%来算,那日均订单约等于50w(500w * 10%)

  3. 如果40%的订单是在秒杀前两分钟完成的,那么每秒产生1200笔订单(50w * 30% / 120s)

  4. 订单支付又涉及到发起支付流程、物流、优惠券、推荐、积分等环节,导致产生大量对象,这里我们假设整个支付流程生成的对象约等于20K,那每秒在Eden区生成的对象约等于20M(1200笔 * 20K)

  5. 在生产环境中,订单模块还涉及到百万商家查询订单、改价、包邮、发货等其他操作,又会产生大量对象,我们放大10倍,即每秒在Eden区生成的对象约等于200M(其实这里就是在大并发场景下可以考虑服务降级的地方,架构其实就是取舍)

这里的假设数据都是大部分电商系统的通用概率,是有一定代表性的。

如果你作为这个系统的架构师,面对这样的场景,你会如何做JVM调优呢?即将运行该系统的JVM堆区设置成多大呢?

前置知识

那做JVM调优需要什么的基础呢?

1、了解Java中基本数据类型的字节长度

public class TestLength {    public static void main(String[] args) {        System.out.println("Byte.SIZE=" + Byte.SIZE / 8);        System.out.println("Character.SIZE=" + Character.SIZE / 8);        System.out.println("Short.SIZE=" + Short.SIZE / 8);        System.out.println("Integer.SIZE=" + Integer.SIZE / 8);        System.out.println("Long.SIZE=" + Long.SIZE / 8);        System.out.println("Float.SIZE=" + Float.SIZE / 8);        System.out.println("Double.SIZE=" + Double.SIZE / 8);    }}

2、深刻理解如何计算对象大小及指针压缩

3、深刻理解JVM内存模型

计算对象大小以及指针压缩,我之前已经写过一篇文章,传送门

看文章毕竟显得枯燥,这里面的很多知识点也不好理解。幸运的是,这两个知识点我在公开课上都有讲过,需要视频的同学可以加小姐姐微信:xyhh620(备注:启明南)

4、动态对象年龄判断

JVM并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。

垃圾判断算法

gc过程中需要知道哪些对象能回收,哪些对象不能回收,那如何判断呢?就是靠这个算法。这里我就不展开讲了,因为学JVM实战的同学我相信对这两种算法都很了解了。如果不了解的同学可以百度搜点文章看看,这块的文章搜出来的基本还算靠谱。

1、引用计数法

2、可达性分析算法(GC Root)

如何调优

这里我们以内存32G服务器来计算。JVM的堆区最大值默认是物理内存的四分之一,即8G。新生代、老年代的比例默认是1:2,即新生代约等于2.7G,老年代约等于5.4G。新生代中Eden区、From区、To区的比例是8:1:1,即Eden区约等于2.2G,From区、To区各占约270M。

前面我们计算了亿级流量并发系统在高峰期时Eden区每秒产生大约200M的对象,如果在部署系统时未对JVM做任何调优,那么系统运行11s左右(2200M),Eden区就会被充满,就会产生young gc。一般来说整个付款环节3s完成算快的,我们这里就按3s来计算,那么在产生young gc时,Eden区有约600M的对象无法回收,因为600M已超过To区的大小,会触发空间担保机制,这600M的对象会直接被移入老年代。按照这个节奏,程序运行一分半种多点就会产生full gc,引起STW,这在付款环节是不可接受的,所以我们需要做调优。

那如何做调优呢?我们需要反向来推理:为了保证不触发空间担保机制,From区、To区都设置成600M,那个新生代就是6G、老年代就是12G。这样一个JVM就吃掉了18G的内存,所以还需要做其他事情:将这个服务器上其他比较吃内存的服务移走,以保证其他服务的正常运行。这里你为了保险起见,你可以设置得更大,具体调优的数值各位童鞋视实际情况而定吧。

这里有个注意点:看网上有些老师的视频或文章,他们调优的时候将老年代设置的大于新生代,我觉得这个方案非常危险。道理同学们可以自己想想。我不建议同学们这样做。

这篇文章就把预估调优堆区这件事情讲清楚了,下篇咱们再将线上产生OOM调优讲清楚。

恭喜你,对JVM的预估调优GET到了正确的姿势。意犹未尽,还想看子牙老师后续的文章?那就关注一波吧。

4a09be8cf8bd161bc8d570cf064fe544.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一、什么是JVM  JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。 JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。JVM在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行。  Java语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java的能够“一次编译,到处运行”的原因。二、JVM的组成我们先把JVM这个虚拟机画出来,如下图所示:从这张图中我们可以看出,JVM是运行在操作系统之上的,它与硬件没有直接的交互,我们再来看JVM由哪些部分组成,如下图所示:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值