Jvm调优实战

Jvm实战调优

OOM(Out Of Memory) 内存溢出错误

ps:由于Java虚拟机有许多实现,本文主要阐述的是OpenJDK的HotSpot虚拟机


一、首先要明白造成OOM错误的场景有哪几种?

场景一:

Java堆溢出,即JVM的内存区域堆空间不足引起的错误。

  • 报错信息:
    “java.lang.OutOfMemoryError: Java heap space”。

  • 原因:
    这是OOM最常见的一种情况,原因是因为堆空间不足,而造成堆空间不足的原因多种多样,如果你的Jvm参数设置合理,那么一般就需要考虑
    是由于代码中存在大量无法被正常回收的对象,也就是内存泄漏引起的。

  • 解决手段:
    通过工具分析内存快照文件,来定位出造成堆溢出的对象。那么首先需要获取内存快照文件,然后在进行定位分析。

1、使用命令jmap -dump:format=b,file=F:\StudyFiles\jvm\xxxx-20210817.hprof 7708 输出dump文件,其中7708是Jvm进程id, 或者也可以使用工具MAT动态acquire截取。

2、使用java自带的jvisualvm进行分析,或者也可以使用Eclipse Memory Analyzer进行分析。需要做的就是导入文件,然后通过工具查看泄露对象到GC Roots的引用链,根据泄露对象的类型引用链一般能够准确的找到对象创建的位置。那么就找到了内存泄漏的具体位置。然后根据代码的功能和产生OOM的场景去修复问题。

3、另外,还可以阿里的Arthas对服务进行监控,Arthas是一款强大的Java诊断工具,下面是Arthas Dashboard ,其中对Thread CPU Memory一目了然,而且还可以对调用栈进行跟踪,调用链的时长进行分析。
Jvm调优实战

4、除了上述基本的手段,推荐一个在线分析GC的网站 GCEasy , 使用方法很简单,进入网站,先将自己的内存快照打成压缩包,然后上传,即可观察到分析结果,而且分析准确率高达80,并且还会针对gc提出优化建议。是一个不错的网站。

5、如果通过上述手段并没有发现存在内存泄漏的对象,大对象都是符合预期的存在,那么就要考虑JVM的堆参数 (-Xmx最大堆内存 -Xms最小堆内存),同时检查机器内存,是否可以继续上调参数。也可以查看大对象的生命周期是否符合预期,存储结构是否能做优化,从代码设计上进行优化。


场景二:

Java栈溢出,分为虚拟机栈溢出和本地方法栈溢出。

  • 报错信息:
    “java.lang.*Error”。

  • 原因:
    这是由于栈空间不足导致的报错,原因在于栈内存无法分配满足需求。
    首先明白,Jvm虚拟机栈是和线程的生命周期一致的,用来保存线程中方法调用的信息。
    Java虚拟机栈溢出有两种可能性:
    1、栈分配的时候,空间不够导致*Error,这种情况一般是由于,方法内部定义了大本地变量,增加了栈帧中本地变量表的长度。
    2、运行时,方法死递归调用,每个方法就是一个栈帧,虚拟机栈不断压栈,最终会导致栈空间不足*Error。

  • 解决手段:
    一般出现*Error,会有明确的堆栈信息打印,很容易就可以定位到是哪个栈帧在入栈时,栈空间不足导致溢出。针对这个方法我们再进一步做分析,到底哪一步出了问题。

场景三:

Java堆溢出,由于无限制创建线程,虚拟机栈一直申请创建空间,导致压缩Jvm整体空间,最终导致Jvm空间不足。

  • 报错信息:
    “java.lang.OutOfMemoryError:unable to create native thread”。

  • 原因:
    操作系统分配给每个进程的内存空间有限制,而Jvm中堆有最大内存限制,而一个线程会创建一个Java虚拟机栈,无限制的创建线程,那么最终会导致Jvm内存不足。

  • 示例代码:

/**
 * 32操作系统分配每个进程的大小大约是上限2GB,即很快就可以测出OOM。
 * 64操作系统分配给每个进程的
 */
public class JavaVmStackOOMTest {

    private void neverStop() {
        while (true){
            System.out.println("running ");
        }
    }

    public void stackLeakByThread() {
        while (true) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    neverStop();
                }
            });
            thread.start();
        }
    }

    public static void main(String[] args) {
        JavaVmStackOOMTest javaVmStackOOMTest = new JavaVmStackOOMTest();
        javaVmStackOOMTest.stackLeakByThread();
    }

}
  • 解决手段:
    出现

Jvm调优实战

上一篇:通过CDS取出来的数量字段为0,SE16N查看又有值.


下一篇:c#开发Mongo笔记第一篇