面试题:工作中做过 JVM 调优吗?怎么做的?

news2024/10/6 16:19:57

文章目录

  • 前言
  • cpu占用过高
  • 死锁
  • 内存泄漏
    • 上面只是其中一种处理方法
  • 总结


前言

最近很多小伙伴跟我说,自己学了不少JVM的调优知识,但是在实际工作中却不知道何时对JVM进行调优。今天,我就为大家介绍几种JVM调优的场景。

在阅读本文时,假定大家已经了解了运行时的数据区域和常用的垃圾回收算法,也了解了Hotspot支持的垃圾回收器。


cpu占用过高

cpu占用过高要分情况讨论,是不是业务上在搞活动,突然有大批的流量进来,而且活动结束后cpu占用率就下降了,如果是这种情况其实可以不用太关心,因为请求越多,需要处理的线程数越多,这是正常的现象。话说回来,如果你的服务器配置本身就差,cpu也只有一个核心,这种情况,稍微多一点流量就真的能够把你的cpu资源耗尽,这时应该考虑先把配置提升吧。

第二种情况,cpu占用率长期过高,这种情况下可能是你的程序有那种循环次数超级多的代码,甚至是出现死循环了。排查步骤如下:

(1)用top命令查看cpu占用情况

在这里插入图片描述

这样就可以定位出cpu过高的进程。在linux下,top命令获得的进程号和jps工具获得的vmid是相同的:

在这里插入图片描述
(2)用top -Hp命令查看线程的情况

在这里插入图片描述

可以看到是线程id为7287这个线程一直在占用cpu

(3)把线程号转换为16进制

[root@localhost ~]# printf "%x" 7287
1c77

记下这个16进制的数字,下面我们要用

(4)用jstack工具查看线程栈情况

[root@localhost ~]# jstack 7268 | grep 1c77 -A 10
"http-nio-8080-exec-2" #16 daemon prio=5 os_prio=0 tid=0x00007fb66ce81000 nid=0x1c77 runnable [0x00007fb639ab9000]
   java.lang.Thread.State: RUNNABLE
 at com.spareyaya.jvm.service.EndlessLoopService.service(EndlessLoopService.java:19)
 at com.spareyaya.jvm.controller.JVMController.endlessLoop(JVMController.java:30)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:498)
 at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
 at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
 at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)

通过jstack工具输出现在的线程栈,再通过grep命令结合上一步拿到的线程16进制的id定位到这个线程的运行情况,其中jstack后面的7268是第(1)步定位到的进程号,grep后面的是(2)、(3)步定位到的线程号。

从输出结果可以看到这个线程处于运行状态,在执行com.spareyaya.jvm.service.EndlessLoopService.service这个方法,代码行号是19行,这样就可以去到代码的19行,找到其所在的代码块,看看是不是处于循环中,这样就定位到了问题。

死锁

死锁并没有第一种场景那么明显,web应用肯定是多线程的程序,它服务于多个请求,程序发生死锁后,死锁的线程处于等待状态(WAITING或TIMED_WAITING),等待状态的线程不占用cpu,消耗的内存也很有限,而表现上可能是请求没法进行,最后超时了。在死锁情况不多的时候,这种情况不容易被发现。

可以使用jstack工具来查看

(1)jps查看java进程

[root@localhost ~]# jps -l
8737 sun.tools.jps.Jps
8682 jvm-0.0.1-SNAPSHOT.jar

(2)jstack查看死锁问题

由于web应用往往会有很多工作线程,特别是在高并发的情况下线程数更多,于是这个命令的输出内容会十分多。jstack最大的好处就是会把产生死锁的信息(包含是什么线程产生的)输出到最后,所以我们只需要看最后的内容就行了

Java stack information for the threads listed above:
===================================================
"Thread-4":
 at com.spareyaya.jvm.service.DeadLockService.service2(DeadLockService.java:35)
 - waiting to lock <0x00000000f5035ae0> (a java.lang.Object)
 - locked <0x00000000f5035af0> (a java.lang.Object)
 at com.spareyaya.jvm.controller.JVMController.lambda$deadLock$1(JVMController.java:41)
 at com.spareyaya.jvm.controller.JVMController$$Lambda$457/1776922136.run(Unknown Source)
 at java.lang.Thread.run(Thread.java:748)
"Thread-3":
 at com.spareyaya.jvm.service.DeadLockService.service1(DeadLockService.java:27)
 - waiting to lock <0x00000000f5035af0> (a java.lang.Object)
 - locked <0x00000000f5035ae0> (a java.lang.Object)
 at com.spareyaya.jvm.controller.JVMController.lambda$deadLock$0(JVMController.java:37)
 at com.spareyaya.jvm.controller.JVMController$$Lambda$456/474286897.run(Unknown Source)
 at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

发现了一个死锁,原因也一目了然。

内存泄漏

我们都知道,java和c++的最大区别是前者会自动收回不再使用的内存,后者需要程序员手动释放。在c++中,如果我们忘记释放内存就会发生内存泄漏。但是,不要以为jvm帮我们回收了内存就不会出现内存泄漏。

程序发生内存泄漏后,进程的可用内存会慢慢变少,最后的结果就是抛出OOM错误。发生OOM错误后可能会想到是内存不够大,于是把-Xmx参数调大,然后重启应用。这么做的结果就是,过了一段时间后,OOM依然会出现。最后无法再调大最大堆内存了,结果就是只能每隔一段时间重启一下应用。

内存泄漏的另一个可能的表现是请求的响应时间变长了。这是因为频繁发生的GC会暂停其它所有线程(Stop The World)造成的。

为了模拟这个场景,使用了以下的程序

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
    public static void main(String[] args) {
        Main main = new Main();
        while (true) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            main.run();
        }
    }

    private void run() {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            executorService.execute(() -> {
                // do something...
            });
        }
    }
}

运行参数是-Xms20m -Xmx20m -XX:+PrintGC,把可用内存调小一点,并且在发生gc时输出信息,运行结果如下

...
[GC (Allocation Failure)  12776K->10840K(18432K), 0.0309510 secs]
[GC (Allocation Failure)  13400K->11520K(18432K), 0.0333385 secs]
[GC (Allocation Failure)  14080K->12168K(18432K), 0.0332409 secs]
[GC (Allocation Failure)  14728K->12832K(18432K), 0.0370435 secs]
[Full GC (Ergonomics)  12832K->12363K(18432K), 0.1942141 secs]
[Full GC (Ergonomics)  14923K->12951K(18432K), 0.1607221 secs]
[Full GC (Ergonomics)  15511K->13542K(18432K), 0.1956311 secs]
...
[Full GC (Ergonomics)  16382K->16381K(18432K), 0.1734902 secs]
[Full GC (Ergonomics)  16383K->16383K(18432K), 0.1922607 secs]
[Full GC (Ergonomics)  16383K->16383K(18432K), 0.1824278 secs]
[Full GC (Allocation Failure)  16383K->16383K(18432K), 0.1710382 secs]
[Full GC (Ergonomics)  16383K->16382K(18432K), 0.1829138 secs]
[Full GC (Ergonomics) Exception in thread "main"  16383K->16382K(18432K), 0.1406222 secs]
[Full GC (Allocation Failure)  16382K->16382K(18432K), 0.1392928 secs]
[Full GC (Ergonomics)  16383K->16382K(18432K), 0.1546243 secs]
[Full GC (Ergonomics)  16383K->16382K(18432K), 0.1755271 secs]
[Full GC (Ergonomics)  16383K->16382K(18432K), 0.1699080 secs]
[Full GC (Allocation Failure)  16382K->16382K(18432K), 0.1697982 secs]
[Full GC (Ergonomics)  16383K->16382K(18432K), 0.1851136 secs]
[Full GC (Allocation Failure)  16382K->16382K(18432K), 0.1655088 secs]
java.lang.OutOfMemoryError: Java heap space

可以看到虽然一直在gc,占用的内存却越来越多,说明程序有的对象无法被回收。但是上面的程序对象都是定义在方法内的,属于局部变量,局部变量在方法运行结果后,所引用的对象在gc时应该被回收啊,但是这里明显没有。

为了找出到底是哪些对象没能被回收,我们加上运行参数-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap.bin,意思是发生OOM时把堆内存信息dump出来。运行程序直至异常,于是得到heap.dump文件,然后我们借助eclipse的MAT插件来分析,如果没有安装需要先安装。

然后File->Open Heap Dump… ,然后选择刚才dump出来的文件,选择Leak Suspects

在这里插入图片描述

MAT会列出所有可能发生内存泄漏的对象

在这里插入图片描述

可以看到居然有21260个Thread对象,3386个ThreadPoolExecutor对象,如果你去看一下java.util.concurrent.ThreadPoolExecutor的源码,可以发现线程池为了复用线程,会不断地等待新的任务,线程也不会回收,需要调用其shutdown()方法才能让线程池执行完任务后停止。

其实线程池定义成局部变量,好的做法是设置成单例。

上面只是其中一种处理方法

在线上的应用,内存往往会设置得很大,这样发生OOM再把内存快照dump出来的文件就会很大,可能大到在本地的电脑中已经无法分析了(因为内存不足够打开这个dump文件)。这里介绍另一种处理办法:

(1)用jps定位到进程号

C:\Users\spareyaya\IdeaProjects\maven-project\target\classes\org\example\net>jps -l
24836 org.example.net.Main
62520 org.jetbrains.jps.cmdline.Launcher
129980 sun.tools.jps.Jps
136028 org.jetbrains.jps.cmdline.Launcher

因为已经知道了是哪个应用发生了OOM,这样可以直接用jps找到进程号135988

(2)用jstat分析gc活动情况

jstat是一个统计java进程内存使用情况和gc活动的工具,参数可以有很多,可以通过jstat -help查看所有参数以及含义

C:\Users\spareyaya\IdeaProjects\maven-project\target\classes\org\example\net>jstat -gcutil -t -h8 24836 1000
Timestamp         S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
           29.1  32.81   0.00  23.48  85.92  92.84  84.13     14    0.339     0    0.000    0.339
           30.1  32.81   0.00  78.12  85.92  92.84  84.13     14    0.339     0    0.000    0.339
           31.1   0.00   0.00  22.70  91.74  92.72  83.71     15    0.389     1    0.233    0.622

上面是命令意思是输出gc的情况,输出时间,每8行输出一个行头信息,统计的进程号是24836,每1000毫秒输出一次信息。

输出信息是Timestamp是距离jvm启动的时间,S0、S1、E是新生代的两个Survivor和Eden,O是老年代区,M是Metaspace,CCS使用压缩比例,YGC和YGCT分别是新生代gc的次数和时间,FGC和FGCT分别是老年代gc的次数和时间,GCT是gc的总时间。虽然发生了gc,但是老年代内存占用率根本没下降,说明有的对象没法被回收(当然也不排除这些对象真的是有用)。

(3)用jmap工具dump出内存快照

jmap可以把指定java进程的内存快照dump出来,效果和第一种处理办法一样,不同的是它不用等OOM就可以做到,而且dump出来的快照也会小很多。

jmap -dump:live,format=b,file=heap.bin 24836

这时会得到heap.bin的内存快照文件,然后就可以用eclipse来分析了。


总结

以上三种严格地说还算不上jvm的调优,只是用了jvm工具把代码中存在的问题找了出来。我们进行jvm的主要目的是尽量减少停顿时间,提高系统的吞吐量。

但是如果我们没有对系统进行分析就盲目去设置其中的参数,可能会得到更坏的结果,jvm发展到今天,各种默认的参数可能是实验室的人经过多次的测试来做平衡的,适用大多数的应用场景。

如果你认为你的jvm确实有调优的必要,也务必要取样分析,最后还得慢慢多次调节,才有可能得到更优的效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1246560.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

c语言习题1124

分别定义函数求圆的面积和周长。 写一个函数&#xff0c;分别求三个数当中的最大数。 写一个函数&#xff0c;计算输入n个数的乘积 一个判断素数的函数&#xff0c;在主函数输入一个整数&#xff0c;输出是否为素数的信息 写一个函数求n! ,利用该函数求1&#xff01;2&…

基于Leaflet的智慧旅游服务平台离线化支持

目录 前言 一、原始文件说明 1、原始网页依赖 2、原始地图API操作 二、Leaflet离线化支持 1、Leaflet资源引入 2、Leaflet地图初始化 3、去掉地图依赖 三、改造效果展示 总结 前言 在上一篇博文推荐了一款适合做智慧旅游项目的大屏&#xff0c;推荐一款适合做智慧旅…

分享一下我个人的微信小程序

FilesUploadHelper 使用七牛云相关的api和sdk搭建的 用户可以通过配置进行文件上传操作

Echarts+Vue+dataV 首页大屏静态示例Demo

效果图: <template><div class="content bg"><!-- 全屏容器 --><!-- 第一行 --><div class="module-box"><div style="flex: 0 1 30%"><dv-decoration-10 style="height: 5px" /></div…

linux磁盘清理

目录 排查过程1、查看磁盘占用情况2. 按照占用大小进行倒排-当前目录及其子目录3.当前目录磁盘占用情况 清理命令 排查过程 1、查看磁盘占用情况 df -hdf -h 命令用于显示磁盘空间的使用情况&#xff0c;以人类可读的方式呈现&#xff0c;其中&#xff1a;df 是 “disk free”…

学习ComplexHeatmap复杂热图

iMeta | 复杂热图(ComplexHeatmap)可视化文章最新版&#xff0c;画热图就引它_生信宝典的博客-CSDN博客 作者贡献 顾祖光&#xff1a;研究课题的提出和设计&#xff0c; 软件编写&#xff0c;可视化&#xff0c;数据分析&#xff0c;论文编写&#xff0c;修订和审阅。 代码和…

MES管理系统需要与ERP系统协同工作吗

在当前的制造业环境中&#xff0c;信息化、智能化、数字化已经成为了企业转型升级的重要方向。其中&#xff0c;ERP企业管理系统与MES生产管理系统的应用和实施&#xff0c;对于提升企业的运营效率和竞争力具有显著效果。然而&#xff0c;在面对系统工具选择时&#xff0c;许多…

现在可以手动获取真随机数吗?

获取真正的随机数并不像获取伪随机数那样简单&#xff0c;因为真随机数的产生依赖于物理过程或者其他难以预测的现象。在计算机科学中&#xff0c;通常使用的是伪随机数&#xff0c;它们是通过算法生成的&#xff0c;看起来像是随机的&#xff0c;但实际上是可以重现的。 如果…

多线程Thread(初阶二:Thread类及常⻅⽅法)

目录 一、Thread 的常⻅构造⽅法 继承Thread代码&#xff1a; 实现Runnable接口代码: 二、Thread 的⼏个常⻅属性 1、id&#xff1a; 2、获取线程的名字。 3、进程的状态&#xff1a; 4、在java中设置的优先级&#xff0c; 5、是否后台线程&#xff0c; 6、是否存活&a…

ubuntu22.04 arrch64版在线安装maven

脚本 if type -p mvn; thenecho "maven has been installed."elsecd /home/zenglgwget https://dlcdn.apache.org/maven/maven-3/3.9.5/binaries/apache-maven-3.9.5-bin.tar.gz --no-check-certificatetar vxf apache-maven-3.9.5-bin.tar.gz rm -rf /usr/local/mav…

Spark---SparkCore(一)

一、术语与宽窄依赖 1、术语解释 1、Master(standalone):资源管理的主节点&#xff08;进程&#xff09; 2、Cluster Manager:在集群上获取资源的外部服务(例如&#xff1a;standalone,Mesos,Yarn) 3、Worker Node(standalone):资源管理的从节点(进程)或者说管理本机资源的…

js实现数组的上下移动

思路&#xff1a;上移表示index索引位置减去1&#xff0c;下移表示index索引位置增加1。使用数组的splice方法实现。例如上移&#xff1a;splice(元素当前索引位置&#xff0c;1(删除1个元素)&#xff0c;‘元素当前索引位置 - 1’)。

Unity 引擎宣布:自 2024 年起,开发者需支付费用!

Unity引擎宣布的新的收费模式&#xff0c;从2024年1月1日开始&#xff0c;根据游戏的安装量来对开发者进行收费。具体来说&#xff0c;每次游戏被下载时&#xff0c;UnityRuntime也会被安装&#xff0c;因此可能会产生额外的费用。对于开发者来说&#xff0c;需要注意以下几点&…

Linux如何查找某个路径下大于1G的文件

find 命令可以用于在 Linux 或 macOS 系统中查找文件和目录。如果你想查找大于1GB的文件&#xff0c;可以使用 -size 选项结合 参数。以下是一个示例&#xff1a; find /path/to/search -type f -size 1G这里的 /path/to/search 是你要搜索的目录的路径。这个命令将查找该目录…

“java.lang.IllegalStateException: No ConfigurableListableBeanFactory set“,缺少配置

一、错误分析 做品优购项目的运营商安全登录时&#xff0c;运行项目后&#xff0c;浏览器访问模板页&#xff0c;模板页的表格无法正常显示&#xff0c;报错信息如下&#xff1a; SEVERE: StandardWrapper.Throwable java.lang.IllegalStateException: No ConfigurableLista…

让工作效率提升10倍:十大AIGC工具评测【建议收藏】

AI技术的普及已经在近年来不断增长。这种技术已经改变了我们与电脑的互动方式&#xff0c;让我们能够更高效、更自然地完成任务。本文将展示10个基于ChatGPT、GPT-3.5和 GPT-4.0 AI模型构建的最强大的资源&#xff0c;使您更容易充分利用它们的潜力。因此&#xff0c;如果您想利…

[HOW TO]-VirtualBox的虚拟机通过宿主机上网

快速链接: . &#x1f449;&#x1f449;&#x1f449; [专栏目录]-环境搭建安装问题笔记目录 &#x1f448;&#x1f448;&#x1f448; 付费专栏-付费课程 【购买须知】:&#x1f449;&#x1f449;&#x1f449; 个人博客笔记导读目录(全部) &#x1f448;&#x1f448;&a…

AI制作的《大多数普通女孩的一生》——公开教程和工作流

内容来源&#xff1a;JiamigouCn ​这周由AI制作的《大多数普通女孩的一生》&#xff0c;在抖音爆火&#xff0c;获得新华网转发。到目前为止&#xff0c;全网还没有公开教程和工作流&#xff0c;需要花费800-2000购买。 本着AI社区共享原则&#xff0c;我委托公众号“楚思智能…

当内容创作进入 AGI 时代,你也可以成为「神笔马良」

我神笔马良的童话故事我们或多或少都听过&#xff0c;一支神笔在手&#xff0c;想画什么就能画出什么&#xff0c;栩栩如生。创造者的理解力、想象力和创作力都能通过这支神笔释放。 近一年&#xff0c;随着 AIGC 内容生产工具的快速出圈&#xff0c;有人把 Stable Diffusion、…

【T3】畅捷通T3软件数据恢复提示:为备份或还原操作指定的设备太多;只允许指定64个设备。

【问题描述】 畅捷通T3软件还原账套/数据恢复过程中提示&#xff1a; SQL Server为备份或还原操作指定的设备太多&#xff1b;只允许指定64个设备。 【解决方法】 提示这个错误时&#xff0c; 说明是在把从SQLServer2005上备份的数据还原到SQLServer2000数据库上。 即&#x…