Java-Jvm-03-生产相关问题解析(cpu蹦高和OOM分析)

1. CPU蹦高

1.1 方案1

  • top 先查看cpu最高的
    在这里插入图片描述
  • 使用 ps -ef | grep 进程Id 或 jps -l 看一下是怎样的后台程序
    在这里插入图片描述
  • 通过进程定位到具体的线程或者代码
    ps -mp 进程Id -o THREAD,tid,time
    参数解释:
    -m 显示所有线程
    -p pid进程使用cpu时间
    -o 该参数是用户自定义格式
    在这里插入图片描述
  • 1.4 将需要的线程Id转换成16进制格式(英文小写格式) printf "%x\n" 线程Id
    在这里插入图片描述
  • 通过jstak工具查看线程栈帧情况
    jstack 进程Id | grep tid(转换后的16进制线程id) -A60
    -A60 代表前60行
    在这里插入图片描述

1.2 方案2

  • top -c 再按 P 排序
    用top -c 命令找出当前进程的运行列表,按一下 P 可以按照CPU使用率进行排序
    在这里插入图片描述
  • 通过进程找线程 top -Hp 进程Id 再按 P 排序
    在这里插入图片描述
  • 将线程Id转换成16进制 printf "%x\n" 线程Id
    在这里插入图片描述
  • 导出线程快照 ,看下里面情况
    jstack -l 进程id > xxx.stack
    在这里插入图片描述
  • less 一下,然后进去根据 转换后的线程Id查看
    在这里插入图片描述

2. OOM问题

除了程序计数器不会抛出OOM外,其他各个内存区域都可能会抛出OOM。多数情况是老年代内存不足导致

2.1 常见的引起oom的问题原因:

  • 一次性申请对象数量过多: 比如查询mysql数据库,一次性将几百万数据封装到list内,这个时候内存不足,会导致oom产生;
  • 内存资源耗尽未释放: 找到未释放的对象进行释放;
  • 本身资源不够: 堆内存分配不均,这个可以通过 jmp -heap 进程号 查询堆信息

2.2 排查oom问题思路

  • 系统已经oom了,已经挂了;
    • 这个没办法处理,只能提前设置:-XX:+HeapDumpOnOutOfMemoryError -XX:heapDumpPath=xxx
  • 系统运行中还未oom
    • 导出dump文件: jmp -dump -format=b,file=wang.hprof 进程编号
    • 结合jprofile或者 jvisualvm 进行调试
      • 查看最多跟业务有关对象–> 找到GcRoot–>查看线程栈信息

2.3 最常见的OOM情况有以下三种

  • java.lang.OutOfMemoryError: Java heap space java堆内存溢出

此种情况最常见,一般由于内存泄露或者堆的大小设置不当引起。对于内存泄露,需要通过内存监控软件查找程序中的泄露代码,而堆大小可以通过虚拟机参数-Xms,-Xmx等修改。
Java 堆溢出原因

  • 无法在 Java 堆中分配对象

  • 应用程序保存了无法被GC回收的对象。

  • 应用程序过度使用 finalizer。
    解决方案

    • 1.查找关键报错信息,如java.lang.OutOfMemoryError: Java heap space
    • 2.使用内存映像分析工具(如Eclipsc Memory Analyzer或者Jprofiler)对Dump出来的堆储存快照进行分析,分析清楚是内存泄漏还是内存溢出。
    • 3.如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链,修复应用程序中的内存泄漏。
    • 4.如果不存在泄漏,先检查代码是否有死循环,递归等,再考虑用 -Xmx 增加堆大小。
  • java.lang.OutOfMemoryError: PermGen space java永久代溢出
    即方法区溢出了,一般出现于大量Class或者jsp页面,或者采用cglib等反射机制的情况,因为上述情况会产生大量的Class信息存储于方法区。此种情况可以通过更改方法区的大小来解决,
    使用类似-XX:PermSize=64m -XX:MaxPermSize=256m的形式修改。另外,过多的常量尤其是字符串也会导致方法区溢出。

  • java.lang.StackOverflowError 栈溢出
    不会抛OOM error,但也是比较常见的Java内存溢出。JAVA虚拟机栈溢出,一般是由于程序中存在死循环或者深度递归调用造成的,栈大小设置太小也会出现此种溢出。可以通过虚拟机参数-Xss来设置栈的大小。
    栈溢出原因

    • 在单个线程下,栈帧太大,或者虚拟机栈容量太小,当内存无法分配的时候,虚拟机抛出StackOverflowError 异常。
    • 不断地建立线程的方式会导致内存溢出。
      解决方案
    • 查找关键报错信息,确定是StackOverflowError还是OutOfMemoryError
    • 如果是StackOverflowError,检查代码是否递归调用方法等
    • 如果是OutOfMemoryError,检查是否有死循环创建线程等,通过-Xss降低的每个线程栈大小的容量

其他情况:

  • java.lang.OutOfMemoryError: Direct buffer memory 本机直接内存溢出
    ByteBuffer分配8MB直接内存,而JVM参数-XX:MaxDirectMemorySize=5M指定最大是5M,因此发生直接内存溢出。
    直接内存并不是虚拟机运行时数据区的一部分,也不是Java 虚拟机规范中定义的内存区域。但是,这部分内存也被频繁地使用,而且也可能导致OOM。
    在JDK1.4 中新加入了NIO(New Input/Output)类,它可以使用 native 函数库直接分配堆外内存,然后通过一个存储在Java堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆中来回复制数据。
    直接内存溢出原因
    本机直接内存的分配虽然不会受到Java 堆大小的限制,但是受到本机总内存大小限制。
    直接内存由 -XX:MaxDirectMemorySize 指定,如果不指定,则默认与Java堆最大值(-Xmx指定)一样。
    NIO程序中,使用ByteBuffer.allocteDirect(capability)分配的是直接内存,可能导致直接内存溢出。
    解决方案

    • 检查代码是否恰当
    • 检查JVM参数-Xmx,-XX:MaxDirectMemorySize 是否合理。
  • java.lang.OutOfMemoryError: GC overhead limit exceeded
    这个是JDK6新加的错误类型,一般都是堆太小导致的。
    Sun 官方对此的定义:超过98%的时间用来做GC并且回收了不到2%的堆内存时会抛出此异常。
    解决方案

    • 检查项目中是否有大量的死循环或有使用大内存的代码,优化代码。
    • 检查JVM参数-Xmx -Xms是否合理
    • dump内存,检查是否存在内存泄露,如果没有,加大内存。
  • java.lang.OutOfMemoryError-->Metaspace 方法区溢出
    方法区,(又叫永久代,JDK8后,元空间替换了永久代),用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。运行时产生大量的类,会填满方法区,造成溢出。
    方法区溢出原因
    使用CGLib生成了大量的代理类,导致方法区被撑爆
    在Java7之前,频繁的错误使用String.intern方法
    大量jsp和动态产生jsp
    应用长时间运行,没有重启
    解决方案

    • 检查是否永久代空间设置得过小
    • 检查代码是否频繁错误得使用String.intern方法
    • 检查是否跟jsp有关。
    • 检查是否使用CGLib

2.4 方案1: jstack排查问题

  • 使用top命令查看内存占比最高的进程
    在这里插入图片描述
  • 使用 top -Hp 进程Id 查看线程
    在这里插入图片描述
  • 使用 jstack打印栈信息 sudo jstack -F 927 > xxx.log

2.5 方案2: heap dump分析

要dump堆的内存镜像,可以采用如下两种方式:

  • 设置JVM参数-XX:+HeapDumpOnOutOfMemoryError
    设定当发生OOM时自动dump出堆信息。
    不过该方法需要JDK5以上版本。-XX:HeapDumpPath=/Users/weihuaxiao/Desktop/dump/ 生成DUMP文件的路径

  • 使用JDK自带的jmap命令jmap -dump:format=b,file=heap.bin pid
    pid指的是java进程Id heap.bin指的是文件名
    dump堆内存信息后,需要对dump出的文件进行分析,从而找到OOM的原因。常用的工具有:
    mat: eclipse memory analyzer, 基于eclipse RCP的内存分析工具
    jhat:JDK自带的java heap analyze tool,可以将堆中的对象以html的形式显示出来,包括对象的数量,大小等等,并支持对象查询语言OQL,分析相关的应用后,可以通过http://localhost:7000来访问分析结果。不推荐使用,因为在实际的排查过程中,一般是先在生产环境 dump出文件来,然后拉到自己的开发机器上分析,所以,不如采用高级的分析工具比如前面的mat来的高效。
    在这里插入图片描述
    在这里插入图片描述
    这样就启动起来了一个简易的HTTP服务,端口号是7000,尝试一下用浏览器访问一下它,本地的可以通过http://localhost:7000,就可以得到这样的页面:
    在这里插入图片描述
    Show instance counts for all classes (excluding platform) 平台外的所有对象信息
    Show heap histogram 先是堆的统计信息

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alan0517

感谢您的鼓励与支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值