首页 > 系统相关 >JVM虚拟机系统性学习-JVM调优实战之内存溢出、高并发场景调优

JVM虚拟机系统性学习-JVM调优实战之内存溢出、高并发场景调优

时间:2023-12-24 11:02:11浏览次数:38  
标签:虚拟机 回收 大小 XX 调优 GC 垃圾 JVM 空间

调优实战-内存溢出的定位与分析

首先,对于以下代码如果造成内存溢出该如何进行定位呢?通过 jmapMAT 工具进行定位分析

代码如下:

public class TestJvmOutOfMemory {
    public static void main(String[] args) {
        List<Object> list = new ArrayList<>();
        for (int i = 0; i < 10000000; i++) {
            StringBuilder str = new StringBuilder();
            for (int j = 0; j < 1000; j++) {
                str.append(UUID.randomUUID().toString());
            }
            list.add(str.toString());
        }
        System.out.println("ok");
    }
}


设置虚拟机参数如下:

-Xms8m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError


再执行上边代码,发现执行之后,发生了内存溢出,并且在当前项目的目录下产生了 java_pid520944.hprof 文件

使用 MAT 工具分析

https://eclipse.dev/mat/downloads.php 中下载 MAT 工具,MAT 工具就是用于分析 Java 堆内存的,可以查看内存泄漏以及内存使用情况

JVM虚拟机系统性学习-JVM调优实战之内存溢出、高并发场景调优_JVM

下载解压之后,点击 exe 文件启动 MAT 工具,将生成的 hprof 文件拖入即可,那么通过 MAT 工具可以看到



调优实战-高并发场景调优

首先,说明一下业务场景,系统主要与用户交互,并且主要是提供 API 服务,因此对于系统延时比较敏感,存在的问题为,发现该系统在高峰期延时过高,通过监控平台发现以下问题:

  • Young GC 比较频繁,每 10 分钟有 50-60 次,峰值达到 400 次
  • Full GC 比较频繁,每 1 个小时平均一次,峰值为 10 分钟 5 次

那么首先排除代码层面的问题,之后再来看 JVM 参数配置所存在的问题,项目使用 JDK8,调优前 JVM 参数如下:

# 设置了堆大小为 4G,新生代大小为 1G
-Xms4096M -Xmx4096M -Xmn1024M
# 设置了永久代大小为 512M,但是并不会生效,因为 JDK8 中使用元空间来实现方法区,永久代已经不使用了,因此下边这两个参数没有起作用
-XX:PermSize=512M
-XX:MaxPermSize=512M


存在问题

问题1:未设置垃圾回收器

从配置的 JVM 参数中可以看到,并未指定使用的垃圾回收器,在 JDK8 中默认使用的垃圾回收器为:(可以在命令行通过 java -XX:+PrintCommandLineFlags -version 来查看 JDK 默认的一些配置信息)

  • 年轻代使用 Parallel Scavenge
  • 老年代使用 Parallel Old

这个组合的垃圾回收器是以 吞吐量优先 的,适合于后台任务型服务器,但是当前服务是与用户进行交互的,因此需要使用 低延迟优先 的垃圾回收器


问题2:年轻代分配不合理

当前系统主要是向外提供 API,那么系统中大多数对象的生命周期都是比较短的,通过 Young GC 都可以进行回收,但是目前的 JVM 配置给堆空间分配了 4G,新生代只有 1G,而新生代又分为 Eden 和 Survivor 区,因此新生代有效大小为 Eden + 一个 Survivor 区,也就是 0.9 G

那么在服务高负载的情况下,新生代中的 Eden + Survivor 区会迅速被占满,进而导致频繁 Young GC,还会引起本应该被 Young GC 回收的垃圾提前晋升到老年代中,导致 Full GC 的频率增加,老年代使用的 Parallel Old 无法与用户线程并发执行进行垃圾回收,因此 STW 时间比较长


问题3:未设置元空间大小

调优前设置了永久代大小,但是 JDK8 中已经废弃了永久代,因此设置永久代大小无效

对于 JDK8 来说,如果不指定元空间的大小,在 64 位操作系统中,默认元空间初始值为 21MB,默认元空间的最大值是系统内存的大小,初始未给定的元空间的大小,因此元空间初始为 21MB,导致 频繁触发 Full GC 来扩张元空间大小


优化方案

首先,针对垃圾回收器,常用的组合如下:

  • Parallel Scavenge + Parallel Old:吞吐量优先,适合后台任务型服务
  • ParNew + CMS:低延迟优先,适合对延迟时间比较敏感的服务
  • G1:JDK9 默认垃圾回收器,兼顾了高吞吐量和低延迟
  • ZGC:JDK11 中退出的低延迟垃圾回收器,无论堆空间多大,都可以保证低延迟

因此,对于目前的系统选择 ParNew + CMS 的组合

而元空间大小的设置,可以通过监控查看元空间峰值为多少,也可以通过命令 jstat -gc [进程id] 查看元空间占用在 150MB 左右,因此可以将元空间大小设置为 256MB

对于年轻代的设置,我们可以考虑在堆空间大小不变的情况下,将新生代空间扩展为 0.5 ~ 1 倍,可以分别扩展 0.5 倍、1 倍,再对扩展后的应用进行压测分析,来选择表现性能更好的方案,这里我们就将年轻代扩展 0.5 倍


优化后的参数设置如下:

# 新生代扩展 0.5 倍
-Xms4096M -Xmx4096M -Xmn1536M
# 初始元空间大小设置为 256M
-XX:MetaspaceSize=256M
-XX:MaxMetaspaceSize=256M
# 使用 ParNew + CMS 垃圾回收器
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
# CMS 在重新标记阶段,会暂停用户线程,重新扫描堆中的对象,进行可达性分析,标记活着的对象,因为并发阶段 GC 线程和用户线程是并发执行的,可能有些对象的状态会因为用户线程的执行而变化,因此在重新标记节点需要进行标记修正,重新标记阶段会以新生代中的对象作为 GC Roots 的一部分,通过开启下边这个参数会在重新标记之前先执行一次 YoungGC 可以回收掉大部分的新生代对象,从而减少扫描 GC Roots 的开销
-XX:+CMSScavengeBeforeRemark


优化方案发布

通过灰度发布,选择部分实例上线,当线上实例指标符合预期之后,再进行全量升级

标签:虚拟机,回收,大小,XX,调优,GC,垃圾,JVM,空间
From: https://blog.51cto.com/u_16186397/8954499

相关文章

  • qemu 虚拟机
    文章目录一、参考资料二、QEMU调试参数三、QEMU命令一、参考资料#查询qemu包aptlist|grepqemu#查询已安装的qemu包aptlist--installed|grepqemu#查询qemu版本qemu-img-V#安装sudoapt-getinstallqemu-system-armqemu-system-mipsqemu-syste......
  • 性能测试之Mysql数据库调优
    一、前言性能调优前提:无监控不调优,对于mysql性能的监控前几天有文章提到过,有兴趣的朋友可以去看一下二、Mysql性能指标及问题分析和定位1、我们在监控图表中关注的性能指标大概有这么几个:CPU、内存、连接数、io读写时间、io操作时间、慢查询、系统平均负载以及memoryOver2、介......
  • 手机平板远程访问kvm虚拟机的windows
    title:手机平板远程访问kvm虚拟机的windowsbanner_img:https://cdn.studyinglover.com/pic/2023/12/5c4ffb4ee8500a9cdfa2406137e5e0a8.jpgdate:2023-12-2319:28:00tags:-踩坑手机平板远程访问kvm虚拟机的windows最近快期末周了,开始陆陆续续开始复习(预习)这学期的课,......
  • JVM虚拟机系统性学习-JVM调优之通过gceasy分析GC日志对堆、元空间、线程堆栈和垃圾回
    通过gceasy工具对生成的GC日志进行分析这里使用的JDK版本为JDK8!在分析GC日志时,可以同时采用多种工具(Arthas、gceasy、JVM连接Graphana监控)进行分析,避免某种工具分析不准确gceasy每个月只可以免费分析5个gc日志,因此要节约机会!hhh!我们先将gc.log文件放入gceasy......
  • JVM虚拟机系统性学习-JVM调优之GC日志分析
    JVM调优首先,为什么要JVM调优呢?JVM调优的目的就是为了让应用程序使用最小的硬件消耗来承载更大的吞吐量什么情况下需要JVM调优呢?系统吞吐量下降,或系统延迟较高出现OOMFullGC频繁GC停顿时间过长(超过1s,已经影响用户体验)调优主要调什么?JVM调优主要是两方面:内存分配和垃圾回......
  • Java JVM面试题
    我分析了上百份大中小厂的面经,整理了Java面试中最最最常问的一些问题!小伙伴们可以对照着网站里面的文章学习或者准备面试。网站的内容会继续完善,欢迎你在评论区说出你遇到的高频面试题!林老师带你学编程(「Java学习+面试指南」是一份涵盖大部分Java程序员所需要掌握的核心知识......
  • 1. JVM
    JVMJVM(JavaVirtualMachine)是一种能够执行Java字节码的虚拟机,是实现Java跨平台特性的核心部分,他屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在JVM上运行的目标代码(字节码),就可以在多种平台上不加修改地运行JDK、JRE与JVMJDK(JavaDevelopmentKit):Java......
  • JVM基础篇(三)-JVM结构-运行时数据区之栈帧
    栈帧栈帧的内部结构每个栈帧中存储着:局部变量表(LocalVariables)操作数栈(operandStack)(或表达式栈)动态链接(DynamicLinking)(或指向运行时常量池的方法引用)方法返回地址(ReturnAddress)(或方法正常退出或者异常退出的定义)一些附加信息并行每个线程下的栈都是私有的,因此每个线程都有自己各......
  • java中对于jvm虚拟机进程的操作类
    在java中,Runtime类表示运行时操作类,是一个封装了JVM进程的类,每一个JVM都对应着一个Runtime类的实例,此实例由JVM运行时为其实例化。下面图示演示了其方法以及应用。......
  • JVM实战-G1参数调优
    G1简介G1GC,全称Garbage-FirstGarbageCollector,在JDK1.7中引入了G1GC,从JAVA9开始,G1GC是默认的GC算法。通过-XX:+UseG1GC参数来启用。G1收集器有分区概念,是工作在堆内不同分区上的收集器。G1的分区既可以是年轻代也可以是老年代,同一个代的分区不需要连续。G1收集器在运行过......