【JVM】亿级流量调优(二)

2024-08-25 13:52
文章标签 java jvm 流量 调优 亿级

本文主要是介绍【JVM】亿级流量调优(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

亿级流量调优

指针压缩

-XX:-UseCompressedOops指针压缩技术只有64位机器才有。jdk6以后引入的技术,默认是开启的

关闭指针压缩的情况下

在这里插入图片描述
在这里插入图片描述

通过HSDB用Memory Viewer查看该对象在内存中的分配地址发现类型指针占8字节,0x3其实是数组的长度,前面用一行来存储类型指针

开启指针压缩的情况下

在这里插入图片描述
在这里插入图片描述
通过HSDB用Memory Viewer查看该对象在内存中的分配地址发现类型指针占4个字节,开启之后,类型指针和数组长度放在一起了

上面的情况分析如下:

在这里插入图片描述

跟C/C++中的一个联合体有关联

union u {
int i;  // 4B
void p; // 8B
}

该联合体所占大小由最大字节数决定
比如说类型指针:0xffffffff,数组长度为0x00000003,当开启指针压缩时,由于只使用了联合体中的4B,但是联合体本身还是占8B,于是就出现了4B的浪费情况,数组长度占4B,那么把两者拼在一起,就可以节省8B的开销,把数组长度填充到前4B里面,相当于0x0003ffff。

为什么数组对象在关闭指针压缩的情况下有两段填充?不仅要站在学习者的角度去思考问题,更要以设计者的角度去思考

在这里插入图片描述

  • 1.在开启指针压缩的情况下:
    类型指针0xffffffff 8B
    数组长度:0xffff 4B
    用一个8B就存储下了
  • 2.那么在关闭指针压缩的情况下
    类型指针0xffffffff 8B
    数组长度: 0x0000ffff
    由于数组长度只占4B,根据8字节对齐的规范,对于一行内存地址来说,这里就出现了4字节的空间,那么我们知道,数组长度的下一个数据区域就是实例数据,如果说实例数据中正好有一个int类型,那么ok,正好填补到前面的4个字节里面,那如果不是int类型呢?是char类型的呢?虽然说可以存进去,但是作为设计者如何知道这个数据区域存储的实例数据是char类型还是int类型呢?所以说,存进去的话,会比较麻烦,于是需要在对象头的尾部进行对齐填充

指针压缩的底层原理?这个技术能被开发出来归根结底是来自"所有对象大小都必须能被8整除"这条规则。

在这里插入图片描述

8字节对齐的言外之意 8(1 000)
如图所示,现在有三个对象
test1:16B
test2:32B
test3:24B

test1的起始地址: 0B
test2的起始地址:16B
test3的起始地址:48B
他们分别对应的二进制:
test1: 0 000
test2:10 000
test3:110 000
可以看到后三位永远是0,那么JVM在存储的时候,就可以这样存储,向右移3位。>>3
也就是说只存储
test1: 0
test2: 10
test3: 110
然后在使用的时候可以左移3位, << 3,再把它还原回去
根本原因在于"8字节对齐"这条规则

堆内存的32G瓶颈从何而来?

在32位机器中,最大内存可以表示为4G,现在机器普遍已经是64位了,按理说,堆内存可以设置超常的大,为什么还会有32G这个瓶颈之说呢?
原因在于,2的32次方表示的内存为4G,在指针压缩使用时,可以将低位进行左移3位,可表示的最大内存也就是2的35次方=32G.

如何扩容,思路是什么?为什么是8字节对齐?

要突破32G内存瓶颈的话,需要改写8字节对齐这条规则,8字节对齐的话,瓶颈是32G,16字节对齐,瓶颈也会扩大一倍,变成64G.但是这样需要考虑内存的使用率,因为在对齐填充的时候,补充的都是空白地址,也就是说在突破瓶颈的同时,也会带来内存空间的浪费。
现在64位机器,并没有完全使用64位地址来表示,只使用了48位,剩下16位保留。也就是说,64位机器上最大使用的内存是2的48次方,原因在于CPU还没有强大到能处理这么大的内存,受制于CPU的算力

例如:
8字节对齐:17B+7B=24B
16字节对齐:17B +15B= 32B(内存的浪费可能更严重)
另外还有一部分原因是:
32G的内存,那么OOM的Dump文件将会变得非常大,普通机器将没法分析

对于一个正常的GC来说,它的频率应该是
5分钟一次YGC
1天一次FullGC
内存大了,虽然发生FullGC的频率小了,但是单次GC花费的时间更长了

JVM调优

为什么调优?
调优的顺序:避免OOM>FullGC>YGC

  • 1.避免OOM,
  • 2.尽可能减少FullGC,FullGC会引发STW(Stop The World)
    到底调什么?
  • 1.在项目部署到线上之前,基于可能的并发量进行预估调优
  • 2.,在项目运行过程中,部署监控收集性能数据,平时分析日志进行调优
  • 3.线上出现OOM,进行问题排查与调优

实战:亿级流量系统实战

在这里插入图片描述

  • 1.如果每个用户平均访问20个商品详情页,那访客数约定于500w(一亿/20)
  • 2.如果按转化率10%来算,那日均订单约等于50w(500w * 10%)
  • 3.如果30%的订单是在秒杀前两分钟完成的,那么每秒产生1200笔订单(50w*30%/120s)
  • 4.订单支付又涉及到发起支付流程、物流、优惠券、推荐、积分等环节,导致产生大量对象,这里我们假设整个支付流程生成的对象为20K,那么每秒在Eden区生成的对象约等于20M(1200笔 * 20K)
  • 5.在生产环境中,订单模块还涉及到百万商家查询订单、改价、包邮、发货等其他操作,又会产生大量对象,我们放大10倍,即每秒在Eden区生成的对象约等于200M(其实这里就是在大并发时刻可以考虑服务降级的地方,架构其实就是取舍)
    假设响应一个请求的时间为3s(包括付款、扣减库存)
    这里的假设数据都是大部分电商系统的通用概率,是有一定代表性的.如果你作为这个系统的架构师,面对这样的场景,你会如何做JVM调优呢?即将运行该系统的JVM堆区设置成多大呢?

分析:

  • 1.每秒产生200M对象,Eden区的大小为2.2G,相当于11s就会触发YGC(11 x 200 = 2200M对象),由于假设一个请求响应时间为3s,所以发生GC的时候,会有600M(200M * 3 = 600M)对象回收不掉。GC回收的是标记不到的对象,而S0、S1只有270M,放不下这600M对象,进而触发老年代空间担保机制,放入老年代。对于老年代而言,大小为5400M->9次YGC->触发一次FGC.9x11=99s,触发一次FC,这样的频率8G内存不适合的
  • 2.怎么做调优?加内存?怎么加?
    S0、S1的内存区域要大于600M,这样,假设S0/S1都为600M,Eden区为4800M(600 x 8 = 4800),约等于6G,那老年代为12G(6G x 2 = 12G),内存加起来的话,12G+6G=18G,但是内存一般都取2的n次幂,所以取16G即可。

如果不调优,则会出现一直触发FGC,又回收不到可用的内存,进而导致JVM发生崩溃

1.堆到底设置成多大比较合适?堆越大越好吗?

如果堆设置的很小-> GC频率可能很高,GC时间也比较短
设置的很大->GC频率会低,但是GC的时长却增加了
调优不是一蹴而就的,需要逐步调整,并观察JVM的GC情况
2.什么样的系统可以进行JVM调优?
首先要区分出是OLAP(在线分析)系统还是OLTP(在线事务查询)系统,如果是OLAP(在线分析)系统,是没有多大调优空间的,因为一次性要查询大量对象,这个是没有办法做调优的,只能加大内存
3.正常GC单次时长100ms,前端和后端的平衡,不能让前端感到明显卡顿

这篇关于【JVM】亿级流量调优(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1105737

相关文章

在Ubuntu上部署SpringBoot应用的操作步骤

《在Ubuntu上部署SpringBoot应用的操作步骤》随着云计算和容器化技术的普及,Linux服务器已成为部署Web应用程序的主流平台之一,Java作为一种跨平台的编程语言,具有广泛的应用场景,本... 目录一、部署准备二、安装 Java 环境1. 安装 JDK2. 验证 Java 安装三、安装 mys

Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单

《Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单》:本文主要介绍Springboot的ThreadPoolTaskScheduler线... 目录ThreadPoolTaskScheduler线程池实现15分钟不操作自动取消订单概要1,创建订单后

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

SpringCloud集成AlloyDB的示例代码

《SpringCloud集成AlloyDB的示例代码》AlloyDB是GoogleCloud提供的一种高度可扩展、强性能的关系型数据库服务,它兼容PostgreSQL,并提供了更快的查询性能... 目录1.AlloyDBjavascript是什么?AlloyDB 的工作原理2.搭建测试环境3.代码工程1.

Java调用Python代码的几种方法小结

《Java调用Python代码的几种方法小结》Python语言有丰富的系统管理、数据处理、统计类软件包,因此从java应用中调用Python代码的需求很常见、实用,本文介绍几种方法从java调用Pyt... 目录引言Java core使用ProcessBuilder使用Java脚本引擎总结引言python

SpringBoot操作spark处理hdfs文件的操作方法

《SpringBoot操作spark处理hdfs文件的操作方法》本文介绍了如何使用SpringBoot操作Spark处理HDFS文件,包括导入依赖、配置Spark信息、编写Controller和Ser... 目录SpringBoot操作spark处理hdfs文件1、导入依赖2、配置spark信息3、cont

springboot整合 xxl-job及使用步骤

《springboot整合xxl-job及使用步骤》XXL-JOB是一个分布式任务调度平台,用于解决分布式系统中的任务调度和管理问题,文章详细介绍了XXL-JOB的架构,包括调度中心、执行器和Web... 目录一、xxl-job是什么二、使用步骤1. 下载并运行管理端代码2. 访问管理页面,确认是否启动成功

Java中的密码加密方式

《Java中的密码加密方式》文章介绍了Java中使用MD5算法对密码进行加密的方法,以及如何通过加盐和多重加密来提高密码的安全性,MD5是一种不可逆的哈希算法,适合用于存储密码,因为其输出的摘要长度固... 目录Java的密码加密方式密码加密一般的应用方式是总结Java的密码加密方式密码加密【这里采用的

Java中ArrayList的8种浅拷贝方式示例代码

《Java中ArrayList的8种浅拷贝方式示例代码》:本文主要介绍Java中ArrayList的8种浅拷贝方式的相关资料,讲解了Java中ArrayList的浅拷贝概念,并详细分享了八种实现浅... 目录引言什么是浅拷贝?ArrayList 浅拷贝的重要性方法一:使用构造函数方法二:使用 addAll(

解决mybatis-plus-boot-starter与mybatis-spring-boot-starter的错误问题

《解决mybatis-plus-boot-starter与mybatis-spring-boot-starter的错误问题》本文主要讲述了在使用MyBatis和MyBatis-Plus时遇到的绑定异常... 目录myBATis-plus-boot-starpythonter与mybatis-spring-b