千家信息网

如何理解Java中的内存溢出问题

发表于:2024-11-11 作者:千家信息网编辑
千家信息网最后更新 2024年11月11日,这篇文章主要介绍"如何理解Java中的内存溢出问题",在日常操作中,相信很多人在如何理解Java中的内存溢出问题问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"如何理解J
千家信息网最后更新 2024年11月11日如何理解Java中的内存溢出问题

这篇文章主要介绍"如何理解Java中的内存溢出问题",在日常操作中,相信很多人在如何理解Java中的内存溢出问题问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"如何理解Java中的内存溢出问题"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

一、内存溢出原因

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

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

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

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

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

5、启动参数内存值设定的过小;

当然实际情况中内存溢出的原因就太多了。下面我们就对这些原因分类一下:

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

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

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

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

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

不知道各位在B站看见过那个面试经典场景没,在回答java的内存运行数据区结构时,以上的功能作用是一方面,如果回答时把内存溢出问题添加上是一个极大的加分项。

二、内存溢出实例

1、堆溢出

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

public class HeapOOM {     static class User {}  public static void main(String[] args) {    List list = new ArrayList();          while (true) {              list.add(new User());       }  } } /*Exception in thread "main" java.lang.OutOfMemoryError:  GC overhead limit exceeded  at com.fdd.test.HeapOOM.main(HeapOOM.java:11)*/

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

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

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

如果线程请求的栈深度大于虚拟机锁允许的最大深度,将抛出StackOverflowError异常。

如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。

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

public class StackOutOfMemoryError {     public static void main(String[] args) {               test();     }     private static void go() {         System.out.println("StackOverflowError异常");         test();     } } /*Exception in thread "main" java.lang.StackOverflowError  at sun.nio.cs.ext.DoubleByte$Encoder.encodeLoop(DoubleByte.java:617)  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:597)  at java.io.PrintStream.println(PrintStream.java:736)  at com.fdd.test.StackOutOfMemoryError.go(StackOutOfMemoryError.java:11)  at com.fdd.test.StackOutOfMemoryError.go(StackOutOfMemoryError.java:13)*/

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

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.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:18)    at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19)    at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19)    at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19)    at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19)    at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19)    at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19)    at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19)    at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19)    ... */

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

public class JavaMethodAreaOOM {     public static void main(String[] args) {         while (true) {             Enhancer enhancer = new Enhancer();             enhancer.setSuperclass(User.class);             enhancer.setUseCache(false);             enhancer.setCallback(new MethodInterceptor() {                 public Object intercept(Object obj, Method method,                       Object[] args, MethodProxy proxy) throws Throwable {                     return proxy.invokeSuper(obj, args);                 }             });             enhancer.create();         }     }     static class User {} } /*Exception in thread "main"  Exception: java.lang.OutOfMemoryError thrown   from the UncaughtExceptionHandler in thread "main" */

4、本机直接内存溢出

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

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);         }     } }

上面介绍了几个实例,那遇到这种问题如何排查呢?

三、内存溢出排查

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

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

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

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

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

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

最后就是解决了。

第一步,修改JVM启动参数,直接增加内存。

第二步,检查错误日志

第三步,对代码进行走查和分析,找出可能发生内存溢出的位置。

一般情况下代码出错的概率会比较大一些,当然了不同的场景不同错误总是复杂多样的。

到此,关于"如何理解Java中的内存溢出问题"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

内存 方法 问题 对象 数据 线程 代码 实例 原因 计数器 过多 学习 循环 参数 就是 空间 运行 不同 最大 作用 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 网络安全joe哥去世 初中生是否学过数据库 三分钟让你搞懂软件开发 软件开发岗位后续发展的信心 阿勒泰软件开发操作 rust怎么进清过档的服务器 国外https代理服务器 计算机网络技术人员技师证书图片 安装两个服务器系统 清芬数据库 数据库为什么关闭连接超时 意识形态网络安全研判 网络安全隔离装置里有内存么 强大的数据库服务器 北京酷伐网络技术有限公司 后宫秘史服务器维护好几个小时 量化策略工具软件开发现成产品 数据库有没有库存管理 您访问网站未接入网络安全审计 软件开发需要什么内存 mdb数据库如何编辑 戴尔服务器什么级别内存 专插本数据库原理习题 幼儿园网络安全教育课题研究 星之后裔哪个服务器 互联网出现在哪次科技革命 富士康测试软件开发 咋样让我的世界服务器火 软件开发怎么发布 苏州亿搜搜网络技术有限公司
0