Java web应用性能分析之【6种OOM模拟】

136 篇文章 0 订阅

 Java web应用性能分析之【Linux服务器性能监控分析概叙】-CSDN博客

Java web应用性能分析概叙-CSDN博客

Java web应用性能分析之【基准测试】-CSDN博客

Java web应用性能分析之【sysbench基准测试】-CSDN博客

Java web应用性能分析之【CPU飙升分析概述】-CSDN博客

Java web应用性能分析之【CPU飙高分析之MySQL】-CSDN博客

 常见的6中OOM
   1. 堆溢出-java.lang.OutOfMemoryError: Java heap space。 检查大对象
   2. 栈溢出-java.lang.OutOfMemorryError:unable to create new native thread    检查多线程
   3. 栈溢出-java.lang.StackOverFlowError。 检查递归
   4. 元信息溢出-java.lang.OutOfMemoryError: Metaspace。
   5. 直接内存溢出-java.lang.OutOfMemoryError: Direct buffer memory。 排查nio,一般重点检查netty,它里面的nio用直接内存用的多
   6. GC超限-java.lang.OutOfMemoryError: GC overhead limit exceeded。

        验证效果

       

        启动脚本

web@zhouxx:~$ JAVA_OPTS=" -server -Dfile.encoding=UTF-8 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8899 -Dcom.sun.management.jmxremote.authenticate=false -Dapp.config=/home/web/demo/application.yaml -Xms64m -Xmx64m  -Xss1m -XX:MetaspaceSize=16m -XX:MaxMetaspaceSize=64m -XX:+UseG1GC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:+PrintCommandLineFlags -Xloggc:log/gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=log/"


web@zhouxx:~$ cd demo/
web@zhouxx:~/demo$ java $JAVA_OPTS  -jar mydemo-1.0.0-SNAPSHOT.jar 
-XX:CompressedClassSpaceSize=58720256 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=log/ -XX:InitialHeapSize=67108864 -XX:+ManagementServer -XX:MaxHeapSize=67108864 -XX:MaxMetaspaceSize=67108864 -XX:MetaspaceSize=16777216 -XX:+PrintCommandLineFlags -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -XX:ThreadStackSize=1024 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC 

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.2.RELEASE)

2024-04-25 19:53:44.288[] mydemo [main] INFO  com.zxx.study.web.AppMain-55- Starting AppMain on zhouxx with PID 1014 (/home/web/demo/mydemo-1.0.0-SNAPSHOT.jar started by web in /home/web/demo)
2024-04-25 19:53:44.292[] mydemo [main] INFO  com.zxx.study.web.AppMain-651- No active profile set, falling back to default profiles: default
2024-04-25 19:53:45.675[] mydemo [main] INFO  o.s.boot.web.embedded.tomcat.TomcatWebServer-92- Tomcat initialized with port(s): 6002 (http)
2024-04-25 19:53:45.687[] mydemo [main] INFO  org.apache.coyote.http11.Http11NioProtocol-173- Initializing ProtocolHandler ["http-nio-0.0.0.0-6002"]
2024-04-25 19:53:45.689[] mydemo [main] INFO  org.apache.catalina.core.StandardService-173- Starting service [Tomcat]
2024-04-25 19:53:45.689[] mydemo [main] INFO  org.apache.catalina.core.StandardEngine-173- Starting Servlet engine: [Apache Tomcat/9.0.29]
2024-04-25 19:53:45.744[] mydemo [main] INFO  o.a.c.core.ContainerBase.[Tomcat].[localhost].[/]-173- Initializing Spring embedded WebApplicationContext
2024-04-25 19:53:45.745[] mydemo [main] INFO  org.springframework.web.context.ContextLoader-284- Root WebApplicationContext: initialization completed in 1391 ms
2024-04-25 19:53:45.962[] mydemo [main] INFO  com.zxx.study.web.config.DependsOn.SpringContext-45- SpringContext我被创建
2024-04-25 19:53:45.964[] mydemo [main] INFO  com.zxx.study.web.config.DependsOn.DependsService-14- DependsService我在springContext后被创建
2024-04-25 19:53:45.969[] mydemo [main] INFO  com.zxx.study.web.config.DependsOn.CurrentServiceA-14- CurrentServiceA我在dependsService后被创建
2024-04-25 19:53:45.971[] mydemo [main] INFO  com.zxx.study.web.config.DependsOn.CurrentServiceB-14- CurrentServiceB我在springContext后被创建
2024-04-25 19:53:45.971[] mydemo [main] INFO  com.zxx.study.web.config.DependsOn.CurrentServiceB-17- com.zxx.study.web.config.DependsOn.CurrentServiceA
2024-04-25 19:53:45.971[] mydemo [main] INFO  com.zxx.study.web.config.DependsOn.CurrentServiceB-21- CurrentServiceB 出错:No bean named 'CurrentServiceC' available
2024-04-25 19:53:45.972[] mydemo [main] INFO  com.zxx.study.web.config.DependsOn.CurrentServiceC-13- CurrentServiceC我在后被创建
2024-04-25 19:53:45.974[] mydemo [main] INFO  com.zxx.study.web.config.DependsOn.CurrentServiceC-17- com.zxx.study.web.config.DependsOn.CurrentServiceA
2024-04-25 19:53:46.284[] mydemo [main] INFO  o.s.scheduling.concurrent.ThreadPoolTaskExecutor-171- Initializing ExecutorService 'applicationTaskExecutor'
2024-04-25 19:53:46.497[] mydemo [main] INFO  o.s.b.actuate.endpoint.web.EndpointLinksResolver-58- Exposing 2 endpoint(s) beneath base path '/actuator'
2024-04-25 19:53:46.542[] mydemo [main] INFO  org.apache.coyote.http11.Http11NioProtocol-173- Starting ProtocolHandler ["http-nio-0.0.0.0-6002"]
2024-04-25 19:53:46.624[] mydemo [main] INFO  o.s.boot.web.embedded.tomcat.TomcatWebServer-204- Tomcat started on port(s): 6002 (http) with context path ''
2024-04-25 19:53:46.631[] mydemo [main] INFO  com.zxx.study.web.AppMain-61- Started AppMain in 3.073 seconds (JVM running for 4.315)

        6种OOM示例

package com.zxx.study.web.controller;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 常见的6中OOM
 *   1. 堆溢出-java.lang.OutOfMemoryError: Java heap space。
 *   2. 栈溢出-java.lang.OutOfMemorryError:unable to create new native thread
 *   3. 栈溢出-java.lang.StackOverFlowError。
 *   4. 元信息溢出-java.lang.OutOfMemoryError: Metaspace。
 *   5. 直接内存溢出-java.lang.OutOfMemoryError: Direct buffer memory。
 *   6. GC超限-java.lang.OutOfMemoryError: GC overhead limit exceeded。
 *
 * @author zhouxx
 * @Description:
 * @date 2024/4/25  11:49
 */
@RestController
@Slf4j
@Validated
@RequestMapping("/api/v1/oom")
public class OomController {

    @Value("${upload.file-path}")
    private String filePath;

    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/javaHeapSpace")
    public String javaHeapSpace(@RequestParam(value ="name", required = false) String name)  {
        //log.info("hi==========");
        List<OOMObject> list = new ArrayList<>();

        while(true) {
            // 方法不结束,对象不回收,模拟堆溢出
            list.add(new OOMObject());
            if(name!=null&&"zhouxx".equals(name)){
                break;
            }
        }
        return "("+Thread.currentThread().getName()+")serverPort: " + serverPort + "  name:" + name;
    }

    @GetMapping("/stackOOM")
    public String stackOOM(@RequestParam(value ="name", required = false) String name)  {
        //log.info("hi==========");


        while(true) {
            // -Xss 创建线程时申请的栈空间
            // 方法不结束,对象不回收,死循环创建线程模拟栈溢出
            Thread thread = new Thread(() -> {

                while (true) {
                    try {
                        TimeUnit.HOURS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            });
            thread.start();
            if(name!=null&&"zhouxx".equals(name)){
                break;
            }
        }
        return "("+Thread.currentThread().getName()+")serverPort: " + serverPort + "  name:" + name;
    }

    @GetMapping("/stackOverFlow")
    public String stackOverFlow(@RequestParam(value ="name", required = false) String name)  {
        //log.info("hi==========");

         // 死循环递归,每次递归时将数据写入栈,模拟栈溢出
        OOMObject.stackOverFlowErrorMethod(name);
        return "("+Thread.currentThread().getName()+")serverPort: " + serverPort + "  name:" + name;
    }


    @GetMapping("/metaspaceOOM")
    public String metaspaceOOM (@RequestParam(value ="name", required = false) String name)  {
        //log.info("hi==========");


        while(true) {
            // 方法不结束,对象不回收,装在元数据,模拟元数据区溢出
            // (jdk1.8)通过CBLIG大量生成类,导致Meta信息满了;(jdk1.8)
            // (jdk1.7)JDK7的时候使用String.intern()不当,会产生大量常量数据;加载大量的jsp以及动态生成jsp文件。
            // todo   jdk 1.8之后才单独将永久代从堆中独立出来,去掉了永久代。 jdk1.7将字符串常量池从永久代移到堆;jdk1.8把方法区的运行时常量池等信息全部迁移到了本地内存(native memory)
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(OOMObject.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object obj, Method method,
                                        Object[] args, MethodProxy proxy) throws Throwable {
                    return proxy.invokeSuper(obj, args);
                }
            });
            enhancer.create();
            if(name!=null&&"zhouxx".equals(name)){
                break;
            }
        }
        return "("+Thread.currentThread().getName()+")serverPort: " + serverPort + "  name:" + name;
    }

    @GetMapping("/directBufferOOM")
    public String directBufferOOM(@RequestParam(value ="name", required = false) String name)  {
        //log.info("hi==========");
        final int _1M = 1024 * 1024 * 1;
        List<ByteBuffer> buffers = new ArrayList<>();

        while(true) {
            // 方法不结束,对象不回收,模拟直接内存溢出
            // 发生直接内存溢出时,重点排查程序里边是否使用的NIO及NIO,比如Netty,里边的直接内存的配置。
            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1M);
            buffers.add(byteBuffer);
            if(name!=null&&"zhouxx".equals(name)){
                break;
            }
        }
        return "("+Thread.currentThread().getName()+")serverPort: " + serverPort + "  name:" + name;
    }

    @GetMapping("/GCOverheadOOM")
    public String GCOverheadOOM(@RequestParam(value ="name", required = false) String name)  {

        // 创建了一个线程池,如果线程池执行的时候如果核心线程处理不过来的时候会把数据放到LinkedBlockingQueue里边,
        // 也就是堆内存当中。这个时候我们需要检查-Xms -Xmx最小最大堆配置是否合理。
        // 再一个dump出现当前内存来分析一下是否使用了大量的循环或使用大量内存代码。
        ExecutorService executor = Executors.newFixedThreadPool(5);
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            executor.execute(() -> {
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    //do nothing
                }
            });
        }
        return "("+Thread.currentThread().getName()+")serverPort: " + serverPort + "  name:" + name;
    }
}
@Data
class OOMObject {
    String name;
    int age;

    public static void stackOverFlowErrorMethod(String name) {
        if(name!=null&&"zhouxx".equals(name)){
            return;
        }
        stackOverFlowErrorMethod( name);
    }

}

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

-无-为-

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值