【JVM】Java内存溢出分析(堆溢出、栈溢出、方法区溢出、直接内存溢出)

您所在的位置:网站首页 栈溢出的常见两个原因 【JVM】Java内存溢出分析(堆溢出、栈溢出、方法区溢出、直接内存溢出)

【JVM】Java内存溢出分析(堆溢出、栈溢出、方法区溢出、直接内存溢出)

2024-06-20 14:32| 来源: 网络整理| 查看: 265

📫作者简介:小明java问道之路,2022年度博客之星全国TOP3,专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化,文章内容兼具广度、深度、大厂技术方案,对待技术喜欢推理加验证,就职于知名金融公司后端高级工程师。

        

📫 热衷分享,喜欢原创~ 关注我会给你带来一些不一样的认知和成长。

        

🏆 2022博客之星TOP3 | CSDN博客专家 | 后端领域优质创作者 | CSDN内容合伙人

🏆 InfoQ(极客邦)签约作者、阿里云专家 | 签约博主、51CTO专家 | TOP红人、华为云享专家

        

🔥如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主~ 

🍅 文末获取联系 🍅  👇🏻 精彩专栏推荐订阅收藏 👇🏻

专栏系列(点击解锁)

学习路线(点击解锁)

知识定位

🔥Redis从入门到精通与实战🔥

Redis从入门到精通与实战

围绕原理源码讲解Redis面试知识点与实战

🔥MySQL从入门到精通🔥

MySQL从入门到精通

全面讲解MySQL知识与企业级MySQL实战

🔥计算机底层原理🔥

深入理解计算机系统CSAPP

以深入理解计算机系统为基石,构件计算机体系和计算机思维

Linux内核源码解析

围绕Linux内核讲解计算机底层原理与并发

🔥数据结构与企业题库精讲🔥

数据结构与企业题库精讲

结合工作经验深入浅出,适合各层次,笔试面试算法题精讲

🔥互联网架构分析与实战🔥

企业系统架构分析实践与落地

行业最前沿视角,专注于技术架构升级路线、架构实践

互联网企业防资损实践

互联网金融公司的防资损方法论、代码与实践

🔥Java全栈白宝书🔥

精通Java8与函数式编程

本专栏以实战为基础,逐步深入Java8以及未来的编程模式

深入理解JVM

详细介绍内存区域、字节码、方法底层,类加载和GC等知识

深入理解高并发编程

深入Liunx内核、汇编、C++全方位理解并发编程

Spring源码分析

Spring核心七IOC/AOP等源码分析

MyBatis源码分析

MyBatis核心源码分析

Java核心技术

只讲Java核心技术

本文目录

本文导读

一、栈内存溢出

1、死递归

2、线程太多

二、堆内存溢出

1、初始对象太大

2、对象没有回收

3、常量池溢出

三、方法区内存溢出

四、直接内存溢出

总结

本文导读

Java内存溢出的原因为程序在申请内存(堆/栈/方法区/直接内存等等)时,没有足够的内存空间。

内存溢出有几种类型,栈溢出(程序所要求的栈深度过大)、堆溢出(创建对象时如果没有可以分配的堆内存、对象没有被回收)、方法区溢出(Class对象未被释放,Class对象占用信息过多,有过多的Class对象)、直接内存溢出(分配的本地内存大小大于JVM的限制)。

一、栈内存溢出 1、死递归

线程请求的栈深度大于虚拟机所允许的最大深度,将抛出 StackOverflowError,可以写一个死递归程序触发。

Java的栈空间默认是1M大小,可以通过-Xss 调整

public class StackOverFlow { public void test(){ test();//死递归 } public static void main(String[] args)throws Throwable { StackOverFlow javaStack = new StackOverFlow(); javaStack.test(); } } 2、线程太多

线程太多会导致栈溢出,抛出 OutOfMemoryError,例如1个方法运行中的对象占用1M内存。同时5000+个线程运行这个方法,如果机器的内存小于5G的话,那么也会发生内存溢出,这种情况会抛出OOM异常。

public class StackOutOfMemoryError { public static void main(String[] args) { StackOutOfMemoryError test = new StackOutOfMemoryError (); test.oomMethod(); } public void oomMethod(){ while(true){ new Thread(new Runnable() { @Override public void run() { loopMethod(); } }).start();; } } private void loopMethod(){ while(true){ } } } 二、堆内存溢出 1、初始对象太大

创建对象时如果没有可以分配的堆内存,JVM就会抛出 OutOfMemoryError:java heap space 异常。

堆内存可以通过设置JVM初始堆空间和最大堆空间,堆溢出生成快照:

-Xms20M -Xmx20M -XX:+HeapDumpOnOutOfMemoryError

/** * VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError */ public static void main(String[] args) { List list = new ArrayList(); int i=0; while(true){ list.add(new byte[5*1024*1024]); System.out.println("分配次数:"+(++i)); } } 2、对象没有回收

在方法执行中,回收效率不足2%,抛出 OutOfMemoryError: GC overhead limit exceeded。

/** * * VM Args:-Xms10m -Xmx10m -Xmn5m -XX:+PrintGCDetails 堆的大小10M * 堆内存溢出 */ public class HeapOom { public static void main(String[] args) { List list = new LinkedList(); //在方法执行的过程中,它是GCRoots int i =0; while(true){ i++; if(i%10000==0) System.out.println("i="+i); list.add(new Object()); } } } 3、常量池溢出

JDK 1.8中 PermSize 和 MaxPermGen 已经无效,JDK 1.7 和 1.8 将字符串常量由永久代转移到堆中,并且 JDK 1.8 中已经不存在永久代的结论。

对象实际存储在堆上面,最终会产生堆内存溢出(java.lang.OutOfMemoryError: Java heap space),将堆内存设置为:-Xms5m -Xmx5m。

public class ConstantPoolOOMTest { public static void main(String[] args) { List list = new ArrayList(); int i=1; try { while(true){ list.add(UUID.randomUUID().toString().intern()); i++; } } finally { System.out.println("运行次数:"+i); } } } 三、方法区内存溢出

方法区溢出为,java.lang.OutOfMemoryError: Metaspace,一般发生在动态语言,因为动态语言编译后会放在方法区。

在经常动态生产大量Class的应用中,CGLIb字节码增强,动态语言,大量JSP(JSP第一次运行需要编译成Java类),基于OSGi的应用(同一个类,被不同的加载器加载也会设为不同的类)。如果方法区内存不够大的话也会发生java.lang.OutOfMemoryError: Metaspace溢出。

public class MethodAreaOOMTest { public static void main(String[] args) { int i=0; try { while(true){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOMObject.class); enhancer.setUseCache(false); // 借助CGLib直接操作字节码,生成大量的动态类 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(); i++; } } finally{ System.out.println("运行次数:"+i); } } static class OOMObject{ } } 四、直接内存溢出

​直接内存溢出( java.lang.OutOfMemoryError: Direct buffer memory)分配的本地内存大小大于JVM的限制。NIO会使用到直接内存,可以通过NIO来模拟。

设置参数: -XX:MaxDirectMemorySize=100m 

/** * VM Args:-XX:MaxDirectMemorySize=100m * 限制最大直接内存大小100m * 直接内存溢出 */ public class DirectOom { public static void main(String[] args) { // 直接分配128M的直接内存(100M) ByteBuffer bb = ByteBuffer.allocateDirect(128*1024*1204); } } 总结

内存溢出有几种类型,栈溢出(程序所要求的栈深度过大)、堆溢出(创建对象时如果没有可以分配的堆内存、对象没有被回收)、方法区溢出(Class对象未被释放,Class对象占用信息过多,有过多的Class对象)、直接内存溢出(分配的本地内存大小大于JVM的限制)。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3