java应用 cpu,内存飙升如何排查?

3 个回答

  • 十刑
    我依旧待人真诚,但不寄予厚望。
    ### CPU 被打满(CPU 100%)的处理方案 #### 问题描述: 在生产环境中,CPU 占用率达到 100% 是一个常见且严重的问题,可能导致系统无法响应。CPU 100% 的原因可以分为三大类:业务类问题、并发类问题和内存类问题。以下是如何诊断和处理 CPU 占用率过高的场景,并且通过实际工具进行排查和解决。 ### 1. CPU 占用率过高的三大类型和场景 #### 1.1 业务类问题 - **死循环**:例如 `while(true)` 条件导致无限循环,持续占用 CPU。 - **死锁**:进程或线程之间发生死锁,导致 CPU 忙等待。 - **不必要的代码块**:不合理的同步代码块增加了 CPU 的负载。 #### 1.2 并发类问题 - **大量计算密集型任务**:图像处理、复杂计算等任务消耗大量 CPU。 - **大量并发线程**:线程过多,导致 CPU 切换频繁,效率低下。 - **大量上下文切换**:线程上下文频繁切换,消耗大量 CPU 资源。 #### 1.3 内存类问题 - **内存不足**:内存不足导致频繁使用虚拟内存,增加 CPU 负载。 - **频繁 GC**:短生命周期对象频繁创建,导致 GC 频繁触发。 - **内存泄漏**:内存持续占用,导致频繁 GC,进一步增加 CPU 占用。 ### 2. 定位 CPU 占用率高的工具 #### 2.1 使用 `jstack` 定位问题 - **`jstack`** 是 JVM 自带的工具,用于查看 Java 进程的堆栈信息,帮助排查 CPU 占用问题。 - 通过 `top` 命令查看 CPU 占用最高的线程,然后使用 `jstack` 来分析线程的堆栈信息。 ##### 操作步骤: 1. 使用 `top` 命令查看占用 CPU 最高的 Java 进程的 PID。 2. 使用 `top -Hp <PID>` 查看具体线程的 ID。 3. 使用 `jstack -l <PID>` 查看线程的堆栈信息,并定位 CPU 占用高的线程。 #### 2.2 使用 Arthas 工具 - **Arthas** 是一个开源的 Java 诊断工具,提供了更强大的 CPU、线程和内存监控功能。 - Arthas 可以通过命令 `dashboard` 查看系统状态,并使用 `thread` 命令查看线程的堆栈信息。 ##### Arthas 操作步骤: 1. 启动 Arthas 并输入 `dashboard` 查看 CPU 占用最高的线程。 2. 使用 `thread -n 3` 查看 CPU 占用最高的前 3 个线程堆栈信息。 ### 3. CPU 100% 的解决思路与方法论 1. **回滚**:首先考虑回滚到之前正常的版本,以便快速恢复系统稳定性。 2. **分析**:使用工具分析并确定具体的 CPU 占用问题,找到问题代码。 3. **优化代码**:根据分析结果优化算法、减少线程、合理控制对象创建频率。 ### 4. 实例操作:使用 `jstack` 和 `Arthas` 解决 CPU 100% 问题 #### 示例代码: ```java public class CPUHoggingTask implements Runnable { @Override public void run() { while (true) { // 模拟一个持续消耗 CPU 的任务 } } public static void main(String[] args) { new Thread(new CPUHoggingTask()).start(); } } ``` ##### 使用 `jstack` 定位: 1. 运行上述代码后,使用 `top` 命令查看进程 PID 和线程 ID。 2. 使用 `jstack` 打印堆栈信息,找到占用 CPU 的线程并定位问题代码。 ##### 使用 Arthas 定位: 1. 启动 Arthas,输入 `dashboard` 查看 CPU 占用情况。 2. 输入 `thread -n 3` 查看最忙的线程并分析堆栈信息。 ### 5. 死锁导致 CPU 占用高的分析 #### 死锁示例代码: ```java public class DeadlockDemo { private static final Object lock1 = new Object(); private static final Object lock2 = new Object(); public static void main(String[] args) { Thread t1 = new Thread(() -> { synchronized (lock1) { try { Thread.sleep(100); } catch (InterruptedException e) {} synchronized (lock2) {} } }); Thread t2 = new Thread(() -> { synchronized (lock2) { try { Thread.sleep(100); } catch (InterruptedException e) {} synchronized (lock1) {} } }); t1.start(); t2.start(); } } ``` ##### 使用 Arthas 定位死锁: 1. 使用 `thread -b` 命令查看所有被阻塞的线程。 2. 分析死锁位置并解决同步问题。 ### 6. CPU 100% 问题的优化策略 - **死循环**:确保循环有退出条件。 - **死锁**:避免嵌套锁,或者使用非阻塞锁机制。 - **频繁 GC**:优化代码,减少对象的创建,合理调整 JVM 参数。 ### 总结 CPU 100% 问题的根本解决方案是通过工具(如 `jstack`、Arthas)定位问题代码,分析原因后进行优化。面试时,首先表明可以通过回滚解决问题,随后说明使用工具分析问题并进行优化的具体步骤和思路。
    编辑于 2024-11-06 12:33 •IP属地 美国
    举报
    评论
    收藏

  • 大小姐
    重新审视自己,什么使你快乐,什么就是眼下最珍贵。
    1.top命令查看是哪个进程pid导致的飙升, 先输入top命令查看查看那个进程占用过高,记录对应的pid 5373 2.再找出对应的线程输入命令 top -p 5373(pid) 这时只能看一个pid,按H就是这个进程下的线程了。 直接使用这个命令一步到位 top -Hp 5373(pid) 查看进程下线程信息 ![image.png](https://images.codewd.com/1b12d4ad716b16834997685f58eb0839.png) 可以看到线程号为5377,5379,5376,5378 转化为对应的16进制5377(1501),5379(1503),5376(1500),5378(1502) 3.jstack 5373(pid) jstack为java堆栈跟踪工具,目的是找出进程中线程过高的线程。 nid=0x1507与上面线程号比较,对应上就是对应的线程,看看在干嘛。 因为屏幕有限,可能看不全,建议导出堆栈到文件中慢慢看jstack -l 5373(pid) > ./a.txt(路径导出栈文件) jstack pid | grep tid(16进制) grep匹配线程号。 幸运的话能看到业务代码就很easy了。多打印几次,一次不一定打印德出来。 ``` //53行代码 Integer[] array = Integers.random(100000,1,200000); ``` ![image.png](https://images.codewd.com/c30d55eceea0ede605014d0025a88201.png) 如果找到是这样的,cpu占用过高是垃圾回收器占用过高。 ![image.png](https://images.codewd.com/cb6990422ab8507db6ca95ccf85d932d.png) 使用jstat -gc 5373 2000 10 显示GC相关信息,观察gc的频率 (2秒打印一次打印10次)关注gc的频率和时间。 ```sql jstat -gc 18378 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 19968.0 14336.0 0.0 14268.4 1319936.0 672172.2 344064.0 158500.9 145024.0 110813.5 20608.0 12871.0 247 3.224 5 0.794 4.018 ``` ```sql S0C:年轻代中第一个survivor(幸存区)的容量 (字节) S1C:年轻代中第二个survivor(幸存区)的容量 (字节) S0U :年轻代中第一个survivor(幸存区)目前已使用空间 (字节) S1U :年轻代中第二个survivor(幸存区)目前已使用空间 (字节) EC :年轻代中Eden(伊甸园)的容量 (字节) EU :年轻代中Eden(伊甸园)目前已使用空间 (字节) OC :Old代的容量 (字节) OU :Old代目前已使用空间 (字节) MC:metaspace(元空间)的容量 (字节) MU:metaspace(元空间)目前已使用空间 (字节) YGC :从应用程序启动到采样时年轻代中gc次数 YGCT :从应用程序启动到采样时年轻代中gc所用时间(s) FGC :从应用程序启动到采样时old代(全gc)gc次数 FGCT :从应用程序启动到采样时old代(全gc)gc所用时间(s) GCT:从应用程序启动到采样时gc用的总时间(s) ``` full gc频率过高,得打印堆内存,看看是什么占用了大部分的内存。 jmap -heap 5373 可以看到老年代占用太大。我jvm设置的是1g。 ![image.png](https://images.codewd.com/b2fc0bb24385f70829029455d5ce29a4.png) jmap -histo 5373 查看那个对象占用这么多,这个是所有对象的占用。 jmap -histo 5373 | head 20 4.如果是cpu,内存太高了,已经outOfMemoryError了。堆栈都打印不出来了怎么吗? jinfo -flag +HeapDumpOnOutOfMemoryError 5373 #开启outOfMemoryError,自动打印堆栈信息。 然后找到bin/jvisualvm 打开 mac 找到bin目录下搜索jvisualvm双击打开就可以了。点击装载选择java_pid7080.hprof就行了。 ![image.png](https://images.codewd.com/b61920dcb9a5c4a8ea194f7d08a13f68.png) outOfMemoryError 找到error日志第一次打印的 case by 会有错误原因。后面就没有了。后面只有Caused by: java.lang.OutOfMemoryError: Java heap space哪一行错误就没有了。 ```java 2022-02-26 14:23:47.140 febs [http-nio-9530-exec-115] ERROR c.w.aspect.GlobalExceptionHandler - 系统内部异常,异常信息: org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.OutOfMemoryError: Java heap space at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1055) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:61) at org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108) at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137) at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125) at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66) at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449) at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365) at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90) at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83) at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:387) at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362) at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:108) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1598) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:748) Caused by: java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:3181) at java.util.ArrayList.grow(ArrayList.java:267) at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:241) at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:233) at java.util.ArrayList.add(ArrayList.java:464) at com.wenda.controller.wenda.recommend.RecommendQueryController.getRecommendList1(RecommendQueryController.java:61) at sun.reflect.GeneratedMethodAccessor111.invoke(Unknown Source) 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:106) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:61) at org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108) at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137) at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125) at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66) ```
    编辑于 2022-02-26 15:12 •IP属地 天津
    举报
    评论
    收藏1

  • java小王子
    遇见错的人会消耗你,遇见对的人会治愈你。
    1.首先使用top命令查看CPU占用率高的进程的pid。 ```java top - 15:10:32 up 523 days, 3:47, 1 user, load average: 0.00, 0.01, 0.05 Tasks: 95 total, 1 running, 94 sleeping, 0 stopped, 0 zombie %Cpu(s): 1.7 us, 0.5 sy, 0.0 ni, 95.7 id, 2.2 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem : 16267904 total, 6940648 free, 2025316 used, 7301940 buff/cache KiB Swap: 16777212 total, 16776604 free, 608 used. 13312484 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 14103 hadoop 20 0 2832812 203724 18392 S 3.7 1.3 977:08.04 java 14010 hadoop 20 0 2897344 285392 18660 S 0.3 1.8 513:30.49 java 14284 hadoop 20 0 3052556 340436 18636 S 0.3 2.1 1584:47 java 14393 hadoop 20 0 2912460 504112 18632 S 0.3 3.1 506:43.68 java 1 root 20 0 190676 3404 2084 S 0.0 0.0 4:31.47 systemd 2 root 20 0 0 0 0 S 0.0 0.0 0:04.77 kthreadd 3 root 20 0 0 0 0 S 0.0 0.0 0:10.16 ksoftirqd/0 ``` 2.使用top -Hp 进程id获得该进程下各个线程的CPU占用情况,找到占用率最高的线程的pid2, 使用printf "%x\n" pid2命令将pid2转换为16进制的数number。 ```java top - 15:11:01 up 523 days, 3:48, 1 user, load average: 0.00, 0.01, 0.05 Threads: 69 total, 0 running, 69 sleeping, 0 stopped, 0 zombie %Cpu(s): 12.8 us, 0.1 sy, 0.0 ni, 87.0 id, 0.1 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem : 16267904 total, 6941352 free, 2024612 used, 7301940 buff/cache KiB Swap: 16777212 total, 16776604 free, 608 used. 13313188 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 14393 hadoop 20 0 2912460 504112 18632 S 0.0 3.1 0:00.01 java 14411 hadoop 20 0 2912460 504112 18632 S 0.0 3.1 0:01.95 java 14412 hadoop 20 0 2912460 504112 18632 S 0.0 3.1 0:16.18 java 14413 hadoop 20 0 2912460 504112 18632 S 0.0 3.1 0:12.79 java 14414 hadoop 20 0 2912460 504112 18632 S 0.0 3.1 8:09.10 java ``` 3.使用jstack pid获得进程下各线程的堆栈信息,nid=0xnumber的线程即为占用率高的线程,查看它是在执行什么操作。(jstack 5521 | grep -20 0x1596可以获得堆栈信息中,会打印匹配到0x1596的上下20行的信息。) 例如这个线程是在执行垃圾回收: ```java "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f338c01f000 nid=0x1593 runnable ``` JVM相关的异常 1.stackoverflow 这种就是栈的空间不足,就会抛出这个异常,一般是递归执行一个方法时,执行方法深度太深时出现。Java执行一个方法时,会创建一个栈帧来存放局部变量表,操作数栈,如果分配栈帧时,栈空间不足,那么就会抛出这个异常。 (栈空间可以设置-Xss参数实现,默认为1M,如果参数)
    发布于 2022-02-22 08:38 •IP属地 青海
    举报
    评论
    收藏