jvm性能调优 - 07线上应用部署JVM实战_栈内存与永久代预估与设置

简介: jvm性能调优 - 07线上应用部署JVM实战_栈内存与永久代预估与设置

Pre

上一篇给大家基于案例分析了一下,如果我们准备上线一个新的系统,如何根据这个系统未来预估的业务量,访问量,去推算这个系统每秒种的并发量,然后推算每秒钟的请求对内存空间的占用,进而推算出整个系统运行期间的JVM内存运转模型。

然后基于这个推算出来的JVM内存运转模型,再接着去在系统上线前就选择一个合理的机器配置,要多大内存的机器,另外给JVM堆内存空间一个合理的大小。

其实这是一项非常基础的技能,因为对于大厂工程师,每次上线一个新的系统,他可能上线就会面临很大的访问压力

所以必须要学会合理预估内存压力,然后选择对应的机器配置,并且给出合理的内存大小 。



基于案例,说说不合理设置内存的反面示例

说的是一个正面的例子,即如何合理的设置内存大小。

今天来说一个反面的不合理设置内存大小导致的问题, 比如现在我们假设一个前提,就是支付系统因为没有经过合理的内存预估,所以直接选用了1台2核4G的虚拟机来部署了线上系统,而且就只用了一台机器

然后线上JVM给的堆内存大小,仅仅就只有1G,扣除老年代之后,新生代其实就几百MB的内存空间,大家看下图。


好了,接着我们还是用相同的业务压力,就是每天100万交易,高峰期每秒大概100笔支付交易,对应核心的支付订单对象有100个创建出来,每个支付订单对象占据500左右的字节大小,总共就是50kb左右。

然后一笔交易要1秒来处理,所以这100个对象在新生代中存在1秒的期间会被人引用,是无法被回收的。

而且我们之前说过一个全局预估的思路,从核心的支付订单对象扩展开来,拓展到系统里其他的对象中去,起码可以把内存占用扩大了10倍~20倍

比如我们就扩大个20倍好了,那么说明1秒之内,总共会创建出来大概1MB左右的对象,无法被回收。


大促期间,流量激增

其实按照估算出来的内存压力,你这么小的新生代在系统正常运行的情况下,其实还不算什么大问题。

因为每秒新增1MB对象,然后几百秒过后,新生代快满了,自然就会触发Minor GC,回收掉里面99%的垃圾对象。

你要是内存那么小,最多就是发现系统每隔几分钟略微卡顿一下,因为这个时候在进行垃圾回收,会影响系统性能。

但是现在我们假设,如果你的电商系统搞大促活动呢?

一般搞大促活动,很可能导致你的压力瞬间增大10倍,因为平时不来你网站的人,今天都来了。

此时可能会发现,每秒钟你的支付系统不是100笔订单了,可能是每秒钟上千笔订单。

这个时候你的系统压力本身就会很大了,不光是内存,尤其是线程资源、CPU资源,都会几乎打满。内存就更是岌岌可危了。


少数请求需要几十秒处理,导致老年代内存占用变大

咱们就针对内存来分析一下。

现在假设你每秒1000笔交易,那么每秒钟系统对内存的占用增加到10MB以上

我们甚至可以再大胆一点,预估每秒对内存占用达到几十MB,甚至上百MB也可以,因为毕竟大促时流量激增,就一切围绕这来预估。

而且最可怕的一点是,可能你每秒过来的1000笔交易,不再是1秒就可以处理完毕了,因为压力骤增,会导致你的系统性能下降,可能偶尔会出现每个请求处理完毕需要几秒钟,甚至几十秒的时间。

此时我们看下图可能出现什么问题,假设你的新生代里已经积压了很多的数据,都快满了。


然后呢,此时内存里有比如几十MB的对象都被人引用着,因为少数请求突然处理的特别慢。

为什么会处理特别慢?因为压力太大,导致系统性能太差了,如下图。


这个时候,如果你要再次在新生代里分配对象,那么是不是会导致一次Minor GC去回收新生代?

没错,但是可能回收掉大量的对象之后,那少数几十MB的对象还在,因为少数请求特别的慢。

然后很快新生代继续被填满,再次触发Minor GC,然后少数几十MB的对象还在,此时多次之后后,就会被转移到老年代去,如下图。



老年代对象越来越多导致频繁垃圾回收

那么大家思考一下,上述流程如果反复来多次,就是时不时有少数请求特别慢,创建的对象在新生代反复多次没法被回收,然后就会被弄到老年代去

然后后续处理完之后,老年代里的对象就没人引用了,成为了垃圾对象。

经常重复这个流程,老年代里的垃圾对象,是不是就会越来越多?

一旦老年代的垃圾对象越来越多,迟早会满,然后就会触发老年代的垃圾回收,而且这个老年代被占满的频率还很快,可能就会频繁触发老年代的垃圾回收。

大家要知道,老年代的垃圾回收速度是很慢的,后面我们分析。

但是在上述场景下,我们基本可以分析出来,如果你不合理的设置内存,就会导致新生代内存不充足,然后导致很多对象不停的迁移到老年代去,最后导致老年代也要不停的进行垃圾回收。

最后这频繁的垃圾回收,就会极大的影响系统的性能。


小结

通过一个支付系统内存设置过小,然后突发巨大的流量压力,突发的性能抖动,最后导致很多对象长期在新生代被人引用,无法被回收,最后持续进入老年代,最后触发老年代内存都频繁占满,然后老年代都频繁被垃圾回收。

所以大家更能从反面体会到,不合理的预估 业务系统压力,不合理的设置内存大小,就可能会导致很大的问题。


如何合理设置永久代大小?

话说回来,如何合理设置永久代大小呢?

其实一般永久代刚开始上线一个系统,没太多可以参考的规范,但是一般你设置个几百MB,大体上都是够用的

因为里面主要就是存放一些类的信息,后面分析什么样的系统容易出现永久代内存溢出。


如何合理设置栈内存大小

其实这个栈内存大小设置,一般也不会特别的去预估和设置的,一般默认就是比如512KB到1MB,就差不多够了

这就是每个线程自己的栈内存空间,用来存放线程执行方法期间的各种布局变量的。后面介绍栈内存什么时候会发生内存溢出。


思考

自己负责的系统的内存压力,然后就是JVM内存大小是否合理,如果业务暴增100倍,是否会有内存问题。

自己画出核心业务流程图,然后一点点去分析,这是一个非常重要的技能。 其实JVM实战技能里的第一步,就是合理预估系统内存压力,合理设置JVM内存大小。


相关文章
|
2月前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
535 1
|
3天前
|
存储 设计模式 监控
如何快速定位并优化CPU 与 JVM 内存性能瓶颈?
本文介绍了 Java 应用常见的 CPU & JVM 内存热点原因及优化思路。
|
1月前
|
存储 Java 程序员
【JVM】——JVM运行机制、类加载机制、内存划分
JVM运行机制,堆栈,程序计数器,元数据区,JVM加载机制,双亲委派模型
|
1月前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
1月前
|
存储 缓存 监控
Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
本文介绍了Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
187 7
|
2月前
|
Java
JVM内存参数
-Xmx[]:堆空间最大内存 -Xms[]:堆空间最小内存,一般设置成跟堆空间最大内存一样的 -Xmn[]:新生代的最大内存 -xx[use 垃圾回收器名称]:指定垃圾回收器 -xss:设置单个线程栈大小 一般设堆空间为最大可用物理地址的百分之80
|
2月前
|
存储 监控 Java
合理设置JVM堆大小
合理设置JVM堆大小
59 4
|
2月前
|
Java
JVM运行时数据区(内存结构)
1)虚拟机栈:每次调用方法都会在虚拟机栈中产生一个栈帧,每个栈帧中都有方法的参数、局部变量、方法出口等信息,方法执行完毕后释放栈帧 (2)本地方法栈:为native修饰的本地方法提供的空间,在HotSpot中与虚拟机合二为一 (3)程序计数器:保存指令执行的地址,方便线程切回后能继续执行代码
34 3
|
2月前
|
存储 缓存 监控
Elasticsearch集群JVM调优堆外内存
Elasticsearch集群JVM调优堆外内存
64 1
|
2月前
|
Arthas 监控 Java
JVM进阶调优系列(9)大厂面试官:内存溢出几种?能否现场演示一下?| 面试就那点事
本文介绍了JVM内存溢出(OOM)的四种类型:堆内存、栈内存、元数据区和直接内存溢出。每种类型通过示例代码演示了如何触发OOM,并分析了其原因。文章还提供了如何使用JVM命令工具(如jmap、jhat、GCeasy、Arthas等)分析和定位内存溢出问题的方法。最后,强调了合理设置JVM参数和及时回收内存的重要性。