内存问题(二)——内存问题模拟

一、堆栈溢出错误模拟-StackOverflowError

1.1 代码

/**
 * 堆栈溢出错误模拟
 *
 * @author sunzhiqiang
 * @date 2021/12/01 19:19:36
 */
public class _1_StackOverflowErrorMock {
    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            try {
                stackOverflowError();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(() -> {
            int num = 0;
            while (true) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(num);
                num++;
            }
        }).start();
        new CountDownLatch(1).await();
    }

    private static void stackOverflowError() {
        try {
            TimeUnit.NANOSECONDS.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        stackOverflowError();
    }
}

1.2 分析

内存问题(二)——内存问题模拟_第1张图片

最终打印:内存问题(二)——内存问题模拟_第2张图片
内存问题(二)——内存问题模拟_第3张图片

线程1代码抛出异常了,线程2还会运行:
——这里的栈溢出,只是一个栈帧装不下;

代码地址(3_memory):
msb-debug

二、堆内存泄漏模拟,mat分析

Java笔记:MAT使用详解
一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

2.1 模拟代码

/**
 * 内存泄漏模拟
 * jmap -dump:format=b,file=D:/workspace/mashibing/msb-debug/dump.hprof pid
 *
 * @author sunzhiqiang
 * @date 2021/11/26 21:56:42
 */
@Data
public class MemoryLeakMock {
    private List<Object> list = new ArrayList<>();

    public static void main(String[] args) throws InterruptedException {
        MemoryLeakMock memoryLeakMock = new MemoryLeakMock();
        long num = 1 * 1024 * 1024;
        while (num > 0) {
            memoryLeakMock.getList().add(new byte[1024]);
            num--;
        }
        new CountDownLatch(1).await();
    }
}

2.2 分析

内存问题(二)——内存问题模拟_第4张图片
t线程1代码抛出异常了,线程2还会运行;

2.3 导出dump文件

jmap -dump:format=b,file=E:/Work_Space/Mca_Space/msb-debug/dump.hprof pid

2.4 mat分析dump文件

2.4.1 Leak Suspects分析

疑惑:生成的dump.hprof文件,与课上的不一样;
课上的:
内存问题(二)——内存问题模拟_第5张图片

我的:没有提示MemoryLeakMock类的这条;
内存问题(二)——内存问题模拟_第6张图片
内存问题(二)——内存问题模拟_第7张图片
猜测可能跟jvm启动参数有关。

除了mat分析工具,还有jprofile工具,只不过是收费的。

三、元数据空间内存溢出-MataspaceOOMTest

3.1 代码

/**
 * JVM参数
 * -XX:MetaspaceSize=12m -XX:MaxMetaspaceSize=12m
 * 

* java 8及之后的版本使用Metaspace来替代永久代,存放了以下信息: *

* 虚拟机加载的类信息 * 即时编译后的代码 *

* 模拟Metaspace空间溢出 **/ public class _3_MataspaceOOMTest { public static void main(String[] args) { new Thread(() -> { try { while (true) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOMTest.class); enhancer.setUseCache(false); enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> methodProxy.invoke(o, args)); enhancer.create(); } } catch (Throwable throwable) { throwable.printStackTrace(); } }).start(); new Thread(() -> { int num = 0; while (true) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(num); num++; } }).start(); } static class OOMTest { } }

3.2 分析

window系统未成功复现此异常,不知具体什么原因。

四、不能创建线程异常-java.lang.OutOfMemoryError:unable to create new native thread

4.1 代码

/**
 * 高并发请求服务器时,经常出现如下异常:java.lang.OutOfMemoryError:unable to create new native thread
 * native thread异常与对应的平台有关
 * 

* 导致原因: * 1 应用创建了太多线程了,一个应用进程创建多个线程,超过系统承载极限 * 2 服务器并不允许应用程序创建这么多线程,linux系统默认允许单个进程可以创建的线程数是1024个, * 应用创建超过这个数量,就会报java.lang.OutOfmemoryError:unable to create new native thread *

* 解决办法: * 1.想办法降低你应用程序创建线程的数量,分析应用是否真的需要创建这么多线程,如果不是,改代码将线程数量降到最低 * 2.对于有的应用,确实需要创建很多线程,远超过linux系统的默认1024个线程的限制,可以通过修改linux服务器配置,扩大linux默认限制 **/ public class _4_UnableCreateNewThreadDemo { public static void main(String[] args) { for (int i = 0; ; i++) { System.out.println("********i=" + i); new Thread(() -> { try { TimeUnit.SECONDS.sleep(Integer.MAX_VALUE); } catch (InterruptedException e) { e.printStackTrace(); } }, "" + i).start(); } } }

五、java.lang.OutOfMemoryError:Direct buffer memory

5.1 代码

/**
 * 〈一句话功能简述〉
* 配置参数: * -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m *

* 故障现象 * Exception in thread "main" java.lang.OutOfMemoryError:Direct buffer memory *

* 导致原因: * 写NIO程序经典使用ByteBuffer来读取或者写入数据,这是一种基于通道(Channel)与缓冲区(Buffer)的 I/O方式, * 可以使用native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。 * 这样能在一些场景种显著提高性能,因为避免了在Java堆和Native堆种来回复制数据。 *

* ByteBuffer.allocate(capability) 第一种方式是分配JVM堆内存, * ByteBuffer.allocateDirect(capability)第一种方式时分配OS本地内存,由于不需要内存拷贝所以速度相对较快。 *

* 但如果不断分配本地内存,对内存很少使用,那么JVM就不需要执行GC,DirectByteBuffer对象们就不会被回收。 * 这时候对内存重组,但本地内存可能已经使用光了,再次尝试分配本地内存就会出OutOfMemoryError,那程序就直接崩溃了。 **/ public class _5_DirectBufferMemoryDemo { public static void main(String[] args) { System.out.println("配置的maxDirectMemory:" + (sun.misc.VM.maxDirectMemory() / (double) 1024 / 1024) + "MB"); ByteBuffer bb = ByteBuffer.allocateDirect(6 * 1024 * 1024); } }

六、GC overhead limit exceeded

6.1 代码

/**
 * 〈一句话功能简述〉
*

* JVM参数配置演示 * -Xms20m -Xmx20m -XX:+PrintGCDetails *

* GC 回收实践过程时会抛出OutOfMemoryError。过长的定义是,超过98%的时间用来做GC并且回收了不到2%的堆内存 * 连续多次GC都只会收到了不到2%的极端情况下才会抛出。假如不抛出GC overhead limit错误会发生什么情况呢? *

* 那就是GC清理的这么点内存很快会再次填满,迫使GC再次执行,这样就形成了恶性循环, * CPU使用率一直100%,而GC却没有任何成果 **/ public class _6_GCOverheadDemo { public static void main(String[] args) { int i = 0; List<String> list = new ArrayList<>(); try { while (true) { list.add(String.valueOf(++i).intern()); } } catch (Throwable e) { System.out.println("*******i:" + i); e.printStackTrace(); throw e; } } }

参考:OutOfMemoryError系列(2): GC overhead limit exceeded
java.lang.OutOfMemoryError: GC overhead limit exceeded问题分析及解决

七、其他

  • 慢sql为什么影响应用?
    ——占用连接数,导致资源无法释放。
    慢sql的累计,最先会导致数据库服务器cpu飙高。一般数据库cpu利用率都不会超过20%。如果超过20%,一般来说,肯定是有慢sql了,监控慢sql,最方便的就是配一个慢sql日志,每天查一下;

  • 一般情况下,内存溢出,只是异常,不会使jvm进程死掉。

  • rpc接口,传参-2M容量;

  • 为什么阿里代码规范,代码不能超过80行?为什么尽量减少递归?
    ——因为栈没有执行完的时候,栈上所有方法不能释放。如果不释放,恰巧来了请求,内存达到一定阈值,会出发gc。

参考文章:
为什么说递归效率低?
为何java不推荐使用递归?

你可能感兴趣的:(java,jvm,nio)