不会JVM调优怎么进互联网大厂

发布时间:2023-05-06 09:44:28

  如果Java技术在面试中经常被问及,但在实际工作中并不常用,那么JVM调优绝对可以排名第一。每当有同学被问到这个问题,内心的OS大概是这样的:我有几百个QPS系统,有什么好调整的?默认配置使用,如果JVM参数调整不好,系统就会崩溃。想想看。但对于一些高并发、大流量的业务场景,JVM调优是有用的。因此,我个人认为JVM调整可能不像SQL调整或代码优化在工作中使用的那么频繁,甚至在很多情况下,但如果你能承受压力来完成优化,那么你和其他人之间的差异就会显现出来。此外,如果我们想进入一线互联网制造商,JVM调优是我们必须掌握的重要技能。

  那么如何调整JVM呢?我们能学习任何常规吗?本文主要关注如何估计参数和优化JVM,希望对大家的日常工作有所帮助。

不会JVM调优怎么进互联网大厂_JVM

估计比调整更重要为什么需要估计?

  所谓凡事预则立不预则废,JVM调优也是如此。修改现有的在线JVM参数配置或优化代码实际上是一个无助的举动,因为生产环境异常运行必须优化这种方式,以确保在线应用服务能够正常运行,否则将拉程序员出来祭天。但是,如果我们可以在发布和部署服务之前估计服务的容量,然后配置相应的JVM参数,这相当于扼杀摇篮中可能出现的JVM异常。当然,这是最理想的状态,在现实中并不容易做到,但即使我们无法预测如此准确,也总比直接裸奔在线而不进行容量预测要好。所以关于JVM的调优,其实和《孙子兵法》的核心思想差不多,上兵伐谋,其次伐交,其次伐兵,其次攻城。也就是说JVM调优的最高境界是预测不调,战争的最高境界是不战而胜。如何预测

  基本流程预估JVM参数

  在明确了JVM参数估计对生产环境中服务稳定运行的意义后,让我们来看看如何估计JVM参数。首先要分析自己系统的核心业务流程是什么,然后根据核心业务流程结合线上可能的流量,确认核心业务代码中的对象是如何创建和销毁的,最后对相关JVM参数进行有针对性的预测配置。因此,预估JVM参数的基本地址流程如下:

不会JVM调优怎么进互联网大厂_内存溢出_02

  案例驱动

  以下是一个实际的业务场景案例,帮助您更好地了解JVM参数估计的过程。假设有这样一个电子商务平台,它主要由商品中心、订单中心、营销中心、库存中心等子系统组成。电子商务系统的核心业务是用户订购。让我们看看如何根据用户订购购物的业务流程来估计JVM参数。

  假设该平台拥有1亿注册用户和1000万日常用户,这些用户将浏览商品,在电子商务平台上下订单和收货评估,但实际上没有那么多用户真正下订单和购买。如果转化率为10%,则相当于电子商务平台每天下订单100w。此外,一般来说,这些订单主要分布在一天的高峰时间,如中午或晚上。毕竟,我们必须在其他时间忙于工作和其他事情。只有在中午或晚上休息时,我们才有时间在平台上购物。如果我们将用户购买的高峰时间定为3小时,也就是说,在极端情况下,我们将在这三个小时内完成所有订单的生成,即每小时生成33万个订单,每秒生成约92个订单。

  在估计订单对象的大小之前,让我们来看看堆中的对象是由哪些元素组成的。JVM对象的大小主要由对象头、数据和数据三部分组成。对象头和对象的补充基本上变化不大,因此对象的实际大小与对象中的属性直接相关。对象中的属性越多,对象占用的空间就越大。

  Mark Word:主要存储对象本身的运行数据,包括HashCodee、GC分代年龄锁状态标志、线程持有的锁等信息。根据操作系统的位数不同,32位操作系统对应的尺寸为32bit,64位操作系统对应的尺寸为64bit;

  Klass Pointer:根据不同的操作数,指向对象对应的Class对象的内存地址在64位系统中占用8个字节,占用不同的空间;

  Array Length:如果目前的对象是数组对象,那么这里存储的是数组的大小,占用4个字节,如果不是数组对象,则不占用。

不会JVM调优怎么进互联网大厂_JVM_03

  回到我们刚才的案例,我们来具体估计一个订单对象占用了多少内存空间。订单对象主要包括以下属性:订单号、商品号、商品价格、创建时间、付款时间、交货时间等。当然,实际订单可能不止这些属性。我们只解释了估计对象大小的方法,所以我们相应地切割了属性。如果这些属性包含在数据层面,那么数据部分的占用空间大小就是这些属性的总和。总估计不到1kb,但实际考虑到其他各种占用。使用和平台肯定不仅是订单对象,还有库存对象、积分对象、物流对象、营销对象等。因此,我们考虑将对象的总和扩大30倍,即平台上产生的各种对象的总和为30乘以1kb,即30kb。如果每秒产生92个对象,则相当于每秒产生2760kb对象,即约2mb对象。此外,由于电子商务平台上的订单操作将包括订单查询、商品查询等业务,我们将把它们放大10倍,也就是说,JVM每秒增加约20mb对象。对于4核8G服务器,我们可以为服务分配大约3G的堆内存和大约512MB的元空间。

  然而,考虑到电子商务平台上的大促销场景,此时的流量可能是平时的几倍,因此我们实际上需要扩大堆内存中的年轻一代,Eden区可以达到1.6G,Survivor区域可分别为200m。这可以避免因年轻一代空间不足而导致对象提前进入老年,从而影响服务的稳定性。JVM调优思路JVM调优思路

  理想情况下,估计的JVM参数应该可以复制在线业务场景,但如果公司业务发展迅速,业务量迅速扩大,原估计的JVM配置参数可能无法满足在线生产环境的所有情况,因此仍会出现异常情况。JVM异常主要分为代码引起的JVM异常和JVM配置不合理引起的JVM参数和服务器内存配置两类。

  JVM代码异常引起的JVM异常

  代码bug应该是导致JVM异常最常见的情况。在这种情况下,我们只能通过调整代码来优化,因为即使是临时调整JVM参数也只是一个缓慢的解决方案,并没有消除问题。随着时间的推移,业务发展问题仍然会暴露出来。因此,为了解决根本问题,我们仍然需要定位问题代码进行优化。

  所以首先,我们需要能够找到导致JVM异常的代码的哪一部分。一般分为两种情况,一种是内存溢出,另一种是没有内存溢出,但在崩溃的边缘,系统响应缓慢。如果我们配置了它-XX:HeapDumpPath参数,当JVM内存溢出时,您可以在相应的目录中找到hprof文件。如果没有内存溢出,此时我们可以通过操作命令jmap -dump:format=b,file=/tmp/文件名.hprof 手动导出内存快照进行分析。有了hprof文件,我们可以通过MAT工具对内存溢出代码位置进行分析和定位,然后进行有针对性的优化。

  Java代码引起的JVM异常可分为以下几种情况:

  (1)如下图所示,客户端和服务端建立了websocket连接。如果连接没有正常建立,则重新建立连接。如果服务端此时没有关闭连接,新的请求对象将被重新使用。随着时间的积累,JVM中大量的对象来不及回收,JVM无法将新的内存空间分配给服务中的新对象,最终导致JVM内存溢出。由于JVM中积累了成千上万的Requestinfo对象,服务仍在生成新的Requestinfo对象,最终不可避免地会出现OutofmemoryEror异常。通过MAT,我们可以轻松定位内存溢出的代码位置,找出为什么Requestinfo对象被创建后,我们可以进行有针对性的优化。

不会JVM调优怎么进互联网大厂_调优_04

  (2)在实际项目开发过程中,我们必须涉及业务数据查询。如果我们不能很好地控制数据查询的条件或我们自己查询的数据量。然后很容易导致一次性查询大量数据,如果所有这些数据都load到内存,很容易导致内存溢出。因此,一般涉及数据查询的代码应进行相应的处理、分页查询、限制查询数据量或流量查询。简而言之,大量的数据不能一次加载到内存中。

  (3)在for循环或while循环中创建大量对象,最终导致对象在堆内存中积累,这是由于条件控制不好,导致对象不断创建。

  (4)众所周知,JVM运行时,数据区的虚拟机栈是线程独有的。JVM启动后,将固定大小的内存(-XSS参数)分配给每个线程的虚拟机栈。因此,确定了虚拟机栈的深度。如果代码中有不合理的递归代码,虚拟机栈将只进不出,最终导致虚拟机栈的内存空间耗尽,导致Stackoverfloweror。

  当我们知道这些常见的代码结构可能导致JVM异常时,我们在编写项目代码时应该始终保持警惕。写完代码后,回头看看这个代码的对象是如何创建的,是否有大对象缓存,是否有不合理的while循环for循环,是否有可能导致JVM内存溢出。当我们对代码有这样的反思意识时,JVM内存溢出的概率从根本上大大降低,这有利于在线服务的稳定性。

  JVM参数不合理引起的异常

  不合理的在线环境JVM参数直接影响JVM的运行稳定性。我们都知道对象存储在堆内存中,堆内存分为年轻和老年,如果我们设置的年轻一代太小,新的对象将分配在年轻一代对应的堆内存中。当然,对象进入老年堆空间的概率会增加,导致fulll GC的可能性也会大大增加。因此,如果JVM参数设置不合理,一般是堆内存大小、元数据区大小和垃圾回收器。此外,我们需要根据不同的业务场景选择相应的垃圾回收器。如果对停顿时间要求较高,可以考虑G1和ZGC。

  通过以上,我们可以清楚地表明,无论是优化业务代码还是参数优化,实际上都是为了避免在堆中留下太多的对象。可以看出,JVM调优的本质思想其实是生产者-消费者模型,为什么这么说?你看,一方面,随着平台业务的不断发展,JVM中的对象会不断产生,所以平台相当于对象生产者。另一方面,勤劳的小蜜蜂垃圾回收器不断检测哪些对象已经是垃圾对象,然后根据垃圾回收的策略释放内存空间,所以JVM相当于对象消费者。一个生产对象,一个消费对象,这不是生产者消费者模式。因此,从这个角度来看,生产者和消费者之间的动态平衡可以保证JVM的正常运行。如果对象生产快,对象回收慢,会导致内存溢出等JVM异常。因此,JVM调优本质上是通过各种手段构建对象生产和回收的动态平衡。常见的JVM配置参数

  无论是发布部署前的JVM参数估计,还是异常后的参数优化,都需要通过调整JVM对应的参数来完成。因此,我们需要掌握常用的JVM参数项及其含义。

  配置项

  含义

  -Xms

  初始堆大小

  -Xmx

  最大值的初始堆

  -Xmn

  堆中新生代最大值

  -XX:SurvivorRatio

  Survivor区与Eden区的比例

  -XX:NewRatio

  新生代和老年代的比例

  -XX:MetaspaceSize

  初始元空间大小

  -XX:MaxMetaspaceSize

  元空间最大小

  -Xss

  线程虚拟机栈的大小

  -XX:+HeapDumpOnOutOfMemoryError

  打开内存溢出时,内存快照

  -XX:HeapDumpPath=/data/dump/jvm.hprof

  内存快照文件路径 JVM常见垃圾回收器

  随着JDK版本的不断迭代,垃圾回收器也在不断迭代和优化。当然,在不同的业务场景下,我们可以选择不同的垃圾回收器来处理,垃圾回收器也在不断调整和改进回收效率高、停顿时间短的目标。

  垃圾回收器

  引入版本

  特点

  适用场景

  Serial GC

  JDK3

  垃圾回收采用单线程方式,所有应用线程暂停

  适用于小型应用、单CPU系统或不需要高并发场景

  ParNew GC

  JDK3

  Serial 实现GC多线程

  垃圾回收在年轻时代

  Parallel GC

  JDK4

  使用多CPU、多核心系统资源,提高垃圾回收效率

  对数据处理、科学计算等吞吐量有要求的应用场景

  CMS GC

  JDK5

  垃圾回收采用多线程,可缩短应用程序的暂停时间

  适用于响应时间要求的应用场景

  G1 GC

  JDK7

  内存分为Region,可指定停顿时间

  适用于部署早多核CPU大内存机上的大型应用,对停顿时间有一定要求,

  ZGC

  JDK11

  支持大量空间,最大停顿时间不超过10mss

  停顿时间小于100ms 总结

  任何技术优化都是基于对技术原理的深刻理解,JVM调优也是如此。文章中常见的JVM调优手段只是一些技术,关键是了解JVM的运行原理和垃圾回收机制。此外,在调整之前,我们必须弄清楚我们的调整目标是什么。有了目标的指导,我们才能有针对性。事实上,无论是性能优化还是业务优化实际上都有一定的规则可以探索,所有的变化都是通过观察:观察当前的状态;分析:分析整个业务链路,找到优化的方向和转型点;优化:制定优化策略和验证方法进行优化实践。

上一篇 Java如何使用MySQL数据库?
下一篇 线上FullGC问题排查实践——手把手教你排查线上问题 | 京东云技术团队

文章素材均来源于网络,如有侵权,请联系管理员删除。

标签: Java教程Java基础Java编程技巧面试题Java面试题