Java 内存溢出(一)原因、复现、排查

这篇具有很好参考价值的文章主要介绍了Java 内存溢出(一)原因、复现、排查。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

内存溢出: 是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能提供的最大内存。这篇文章整理自《深入理解 java 虚拟机》。

一、内存溢出原因

内存溢出就是内存不够,引起内存溢出的原因有很多种,常见的有以下几种:

  1. 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
  2. 集合类中有对对象的引用,使用完后未清空,使得 JVM 不能回收;
  3. 代码中存在死循环或循环产生过多重复的对象实体;
  4. 使用的第三方软件中的 BUG;
  5. 启动参数内存值设定的过小。

当然实际情况中内存溢出的原因就太多了。

Java 内存溢出(一)原因、复现、排查

以上的图是基于 java7 来描述的,从上面这张图我们能够得到如下信息:java 虚拟机把内存分为 5 个模块。

(1)程序计数器:程序计数器是线程私有的,主要作用是通过改变这个计数器的值来选取下一条需要执行的字节码指令。既然每个线程都有一个,那么这些线程的计数器是互不影响的,也不会抛出任何异常。

(2)虚拟机栈和本地方法栈:虚拟机栈描述的是 java 方法执行的内存模型,每个方法在执行的时候都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。本地方法栈与虚拟机栈的区别是,虚拟机栈为虚拟机执行 java 方法服务,而本地方法栈则为虚拟机提供 native 方法服务。

在单线程的操作中,无论是由于栈帧太大,还是虚拟机栈空间太小,当栈空间无法分配时,虚拟机抛出的都是 StackOverflowError 异常,而不会得到 OutOfMemoryError 异常。而在多线程环境下,则会抛出 OutOfMemoryError 异常。

(3)java 堆和方法区:java 堆区主要存放对象实例和数组等,方法区保存类信息、常量、静态变量等等。运行时常量池也是方法区的一部分。这两块区域是线程共享的区域,只会抛出 OutOfMemoryError。

二、内存溢出实例

1、堆溢出

既然堆是存放实例对象的,那我们就无限创建实例对象。这样堆区迟早会满。

执行时需要设置VM参数:-Xmx10M,限制最大堆内存为10M

import java.util.ArrayList;
import java.util.List;

public class HeapOOM {
    static class User {}
    public static void main(String[] args) {
        List<User> list = new ArrayList<>();
        while (true) {
            list.add(new User());
        }
    }
} 

异常内容:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.util.Arrays.copyOf(Arrays.java:3210)
	at java.util.Arrays.copyOf(Arrays.java:3181)
	at java.util.ArrayList.grow(ArrayList.java:261)
	at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
	at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
	at java.util.ArrayList.add(ArrayList.java:458)
	at com.demo.HeapOOM.main(HeapOOM.java:6)

因为我提前设置了堆区内存限制,所以无限创建就会抛出异常。

2.虚拟机栈和本地方法栈溢出

Java 虚拟机规范中描述了两种异常:

如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出 StackOverflowError 异常。如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出 OutOfMemoryError 异常。

第一种我们只需要使用方法递归调用即可模拟:

public class StackOutOfMemoryError {
    public static void main(String[] args) {
        test();
    }
    private static void test() {
        System.out.println("StackOverflowError 异常测试");
        test();
    }
}

异常内容:

Exception in thread "main" java.lang.StackOverflowError
	at sun.nio.cs.UTF_8$Encoder.encodeLoop(UTF_8.java:691)
	at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:579)
	at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:271)
	at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
	at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
	at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
	at java.io.PrintStream.write(PrintStream.java:526)
	at java.io.PrintStream.print(PrintStream.java:669)
	at java.io.PrintStream.println(PrintStream.java:806)
	at com.demo.StackOutOfMemoryError.test(StackOutOfMemoryError.java:6)

第二种也可以递归调用模拟,但是使用的是类直接调用:

public class JavaVMStackSOF {
    private int stackLength = 1;
    public void stackLeak() {
        stackLength++;
        stackLeak();
    }
    public static void main(String[] args) {
        JavaVMStackSOF oom = new JavaVMStackSOF();
        oom.stackLeak();
    }
}

异常内容:

Exception in thread "main" java.lang.StackOverflowError
	at com.demo.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:5)

3.方法区和运行时常量池溢出

执行时需要设置VM参数:-Xmx10M,限制最大堆内存为10M

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class JavaMethodAreaOOM {
    private static class User {}
    public static void main(String[] args) {
        while (true) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(User.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();
        }
    }
}

异常内容:

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
	at java.lang.AbstractStringBuilder.<init>(AbstractStringBuilder.java:68)
	at java.lang.StringBuffer.<init>(StringBuffer.java:116)
	at org.objectweb.asm.Type.getDescriptor(Unknown Source)
	at net.sf.cglib.core.ClassEmitter.declare_field(ClassEmitter.java:193)
	at net.sf.cglib.proxy.MethodInterceptorGenerator.generate(MethodInterceptorGenerator.java:94)
	at net.sf.cglib.proxy.Enhancer.emitMethods(Enhancer.java:994)
	at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:498)
	at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
	at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
	at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
	at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285)
	at com.demo.JavaMethodAreaOOM.main(JavaMethodAreaOOM.java:22)

4.本机直接内存溢出

执行时需要设置VM参数:-Xmx10M,限制最大堆内存为10M

import sun.misc.Unsafe;

import java.lang.reflect.Field;

public class DirectMemoryOOM {
    private static final int _1MB = 1024 * 1024;
    public static void main(String[] args) throws Exception {
        Field unsafeField = Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);
        Unsafe unsafe = (Unsafe) unsafeField.get(null);
        while (true) {
            unsafe.allocateMemory(_1MB);
        }
    }
}

异常内容:

Exception in thread "main" java.lang.OutOfMemoryError
	at sun.misc.Unsafe.allocateMemory(Native Method)
	at com.demo.DirectMemoryOOM.main(DirectMemoryOOM.java:12)

DirectMemory 容量可通过 -XX:MaxDirectMemorySize指定,如果不指定,则默认与 Java 堆最大值(-Xmx 指定)一样。

三、内存溢出排查

排查最主要的就是检查代码,而且内存溢出往往都是代码的问题。当然以下几点都是需要注意的:

(1)内存中加载的数据量过于庞大,如一次性从数据库取出过多数据;

(2)集合类中有对对象的引用,使用完后未清空,是的 JVM 不能回收;

(3)代码中存在死循环或循环产生过多重复的对象实体;

(4)使用的第三方软件中的 BUG;

(5)启动参数内存值设定的过小。

内存溢出解决方法:

方法一:修改 JVM 启动参数,直接增加内存,暂时解决问题;

方法二:检查错误日志,定位可能发生内存溢出的位置,优化代码;

方法三:如果设置了内存参数 -XX:+HeapDumpOnOutOfMemoryError,当内存溢出时会产生 .hprof 文件(记录了 Java 进程在某个时间内的快照),可以通过 MAT 工具进行分析,优化代码。

整理完毕,完结撒花~





参考地址:

1.java内存溢出,https://blog.csdn.net/u014401141/article/details/122825443文章来源地址https://www.toymoban.com/news/detail-439483.html

到了这里,关于Java 内存溢出(一)原因、复现、排查的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JVM-内存溢出的原因、CPU占满的原因

    OOM的排查思路_oom排查_java排坑日记的博客-CSDN博客 每个进程的内存(限制,譬如2G)=最大堆容量+最大方法区容量+程序计数器+虚拟机栈和本地方法栈。多线程下每个线程栈越大,越容易OOM.                 1)大对象(从数据库里一次请求了大量的数据)         

    2024年02月10日
    浏览(46)
  • jdk17下netty导致堆内存疯涨原因排查

    天网风控 灵玑 系统是基于内存计算实现的高吞吐低延迟在线计算服务,提供滑动或滚动窗口内的count、distinctCout、max、min、avg、sum、std及区间分布类的在线统计计算服务。客户端和服务端底层通过netty直接进行tcp通信,且服务端也是基于netty将数据备份到对应的slave集群。 灵

    2024年02月10日
    浏览(33)
  • Java中的内存泄露、内存溢出与栈溢出

    大家好,我是欧阳方超。本次就Java中几个相似而又不同的概念做一下介绍。内存泄漏、内存溢出和栈溢出都是与内存相关的问题,但它们之间有所不同。 我们经常会遇到内存泄漏、内存溢出和栈溢出等问题,这些问题都与内存的使用有关。 内存泄漏(memory leak)指的是程序

    2024年02月03日
    浏览(72)
  • Java中的内存溢出与内存泄漏深度解析

    目录 引言 一. 内存溢出(Memory Overflow) 1.1 堆内存溢出 1.2 栈内存溢出 1.3 内存溢出的解决策略 1.3.1 优化对象的创建和销毁 1.3.2 调整堆内存大小 1.3.3  使用内存分析工具 1.3.4 避免创建过大的对象 1.3.5 定期清理不再使用的对象 二、 内存泄漏(Memory Leak) 2.1Java内存泄漏的典

    2024年02月19日
    浏览(58)
  • Java jvm 内存溢出分析

    我们经常用visualVm监控Jvm的内存,cpu,线程的使用情况,通常可以根据内存不断增长来判断内存是否存在不释放。但是我们不可能时时盯着去看,这里涉及jvm堆内存配置,堆内存参数配置和调优会在其他章节编写。 如果真是内存溢出了,线上出现的我们需要配置JVm内存溢出,

    2024年02月09日
    浏览(54)
  • 深入理解Java虚拟机(二)Java内存区域与内存溢出异常

            对于Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要为每一个new操作去写配对的delete/free代码,不容易出现内存泄漏和内存溢出问题,看起来由虚拟机管理内存一切都很美好。不过,也正是因为Java程序员把控制内存的权力交给了Java虚拟机,一旦出

    2024年02月16日
    浏览(72)
  • Step2:Java内存区域与内存溢出异常

    对于Java程序员来说,再虚拟机自动内存管理机制的帮助下,不再需要为每一个new操作去写配对的delete/free代码,不容易出现内存泄露和内存溢出的问题,看起来由虚拟机管理内存一切都很美好。不过正是因为Java程序员把控制内存的权利交给来Java虚拟机,一旦出现内存泄露方

    2024年02月07日
    浏览(50)
  • Java 内存溢出(二)使用 MAT 分析 .hprof 内存映像文件

    .hprof 文件: 是 java 项目的 Heap Dump 文件,也叫内存映像文件、内存快照文件,可以存放一个 java 进程在某个时间点的内存快照。生成 Heap Dump 文件的方式有两种:一是使用 jmap 命令手动导出,二是启动脚本中添加 -XX:+HeapDumpOnOutOfMemoryError 参数自动导出。本文中只涉及第二种。

    2023年04月08日
    浏览(70)
  • Java内存溢出问题深入探究及其解决策略

    Java内存溢出是一个常见且棘手的问题,可能会导致程序的性能急剧下降或者崩溃,给业务带来严重的影响。为了深入解析和理解此问题,本文将详细探究Java的内存模型,内存溢出的根本原因,诊断方法以及解决策略。 1.1 Java内存模型 Java内存空间主要包括以下几个部分:方法

    2024年02月09日
    浏览(49)
  • 【Java流式下载大文件,避免OOM内存溢出】

    Java下载文件时,如果是小文件的下载,我们一般直接使用工具类的方法,比如cn.hutool.http.HttpUtil.downloadFile()。但是如果是大文件的下载,使用这些工具类的方法,可能会出现Out of Memory内存溢出,它是指需要的内存空间大于系统分配的内存空间,oom后果就是项目程序crash,Hpr

    2024年02月11日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包