常见的Java内存溢出情况和实例

简介: 常见的Java内存溢出情况和实例



Java开发中或者部署到服务器上后,可能经常发生内存溢出,本文总结下常见的溢出情况。

内存溢出的原因

Java内存溢出的原因:程序在申请内存时,没有足够的内存空间.

内存溢出有几种类型,如下:

栈溢出

栈溢出的第1种情况,死递归,抛出StackOverflowError

如下代码:

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

运行后发生如下异常:

Exception in thread “main” java.lang.StackOverflowError

at com.jvm.ch05.oom.StackOverFlow.test(StackOverFlow.java:13)

at com.jvm.ch05.oom.StackOverFlow.test(StackOverFlow.java:13)

在java虚拟机种,Java的栈空间默认是1M大小(可以通过-Xss调整,一般不用调整),反复调用,会超出栈溢出。

栈溢出的第2种情况,线程太多,抛出OutOfMemoryError

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

堆溢出

1.初始对象太大,超过机器内存,抛出OutOfMemoryError: Java heap space

如下代码:

//VM Args:-Xms30m -Xmx30m -XX:+PrintGCDetails    堆的大小30M
String[] strings = new String[100000000];  //100m的数组(堆)

运行结果:

Exception in thread “main” java.lang.OutOfMemoryError: Java heap space

at com.jvm.ch05.oom.HeapOom.main(HeapOom.java:14)

对象分配内存需要100M.如果机器的内存小于100M就会发生 Java heap space。

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

demo如下:

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

运行结果:

[Full GC (Ergonomics) [PSYoungGen: 4096K->4096K(4608K)] [ParOldGen: 5045K->5045K(5120K)] 9141K->9141K(9728K), [Metaspace: 3356K->3356K(1056768K)], 0.1506820 secs] [Times: user=1.05 sys=0.00, real=0.15 secs]

[Full GC (Ergonomics) [PSYoungGen: 4096K->4096K(4608K)] [ParOldGen: 5047K->5047K(5120K)] 9143K->9143K(9728K), [Metaspace: 3356K->3356K(1056768K)], 0.1767119 secs] [Times: user=1.11 sys=0.00, real=0.18 secs]

[Full GC (Ergonomics) [PSYoungGen: 4096K->4096K(4608K)] [ParOldGen: 5048K->5048K(5120K)] 9144K->9144K(9728K), [Metaspace: 3356K->3356K(1056768K)], 0.1575503 secs] [Times: user=0.99 sys=0.00, real=0.16 secs]

[Full GC (Ergonomics) [PSYoungGen: 4096K->0K(4608K)] [ParOldGen: 5052K->658K(5120K)] 9148K->658K(9728K), [Metaspace: 3356K->3356K(1056768K)], 0.0188752 secs] [Times: user=0.09 sys=0.00, real=0.02 secs]

Heap

PSYoungGen total 4608K, used 169K [0x00000000ffb00000, 0x0000000100000000, 0x0000000100000000)

eden space 4096K, 4% used [0x00000000ffb00000,0x00000000ffb2a498,0x00000000fff00000)

from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)

to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)

ParOldGen total 5120K, used 658K [0x00000000ff600000, 0x00000000ffb00000, 0x00000000ffb00000)

object space 5120K, 12% used [0x00000000ff600000,0x00000000ff6a4b48,0x00000000ffb00000)

Metaspace used 3390K, capacity 4500K, committed 4864K, reserved 1056768K

class space used 364K, capacity 388K, committed 512K, reserved 1048576K

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded

at com.jvm.ch05.oom.HeapOom.main(HeapOom.java:20)

原因: 本例中一直在发生fullgc中,list集合一直在塞入对象,回收内存占比越来越小,虚拟机中有个规则:垃圾回收(线程)占用超过了98%的资源,但是回收效率不足2%,就会发生了’OOM, GC overhead limit exceeded

直接内存溢出 Direct buffer memory

分配的本地内存大小大于JVM的限制.

demo如下:

设置参数: -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);
    }
}

运行结果:

Exception in thread “main” java.lang.OutOfMemoryError: Direct buffer memory

at java.nio.Bits.reserveMemory(Bits.java:694)

at java.nio.DirectByteBuffer.(DirectByteBuffer.java:123)

at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)

at com.jvm.ch05.oom.DirectOom.main(DirectOom.java:13)

虚拟机允许的直接内存为100M.但是ByteBuffer 需要128M的空间,所以发生直接内存溢出OutOfMemoryError: Direct buffer memory。

方法区溢出java.lang.OutOfMemoryError: Metaspace

一般发生在动态语言,因为动态语言编译后会放在方法区。

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

“前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。”


大功告成!!

相关文章
|
8天前
|
存储 Java 测试技术
滚雪球学Java(18):解密JavaSE中的堆栈:你真的了解Java内存吗?
【4月更文挑战第7天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
40 1
滚雪球学Java(18):解密JavaSE中的堆栈:你真的了解Java内存吗?
|
18天前
|
存储 Java 编译器
Java内存区域详解
Java内存区域详解
29 0
Java内存区域详解
|
18天前
|
存储 缓存 Java
优化Java代码中的内存使用:使用WeakHashMap解决内存泄漏问题
在Java应用程序中,内存泄漏是一个常见的问题,尤其是在处理大量对象时。本文将介绍如何使用WeakHashMap数据结构来解决内存泄漏问题,通过示例代码演示其在实际项目中的应用,从而提高Java代码的性能和可靠性。
|
3天前
|
算法 Java Go
Go vs Java:内存管理与垃圾回收机制对比
对比了Go和Java的内存管理与垃圾回收机制。Java依赖JVM自动管理内存,使用堆栈内存并采用多种垃圾回收算法,如标记-清除和分代收集。Go则提供更多的手动控制,内存分配与释放由分配器和垃圾回收器协同完成,使用三色标记算法并发回收。示例展示了Java中对象自动创建和销毁,而Go中开发者需注意内存泄漏。选择语言应根据项目需求和技术栈来决定。
|
3天前
|
Java
【专栏】Java中的反射机制与应用实例
【4月更文挑战第27天】本文探讨了Java反射机制,该机制允许程序在运行时获取类信息、动态创建对象、调用方法和访问属性。反射通过Class、Constructor、Method和Field类实现。文中列举了反射的应用场景,如动态创建对象、调用方法、访问属性和处理注解,并提供了相关实例代码演示。
|
21天前
|
缓存 Java
Java中循环创建String对象的内存管理分析
Java中循环创建String对象的内存管理分析
22 2
|
24天前
|
缓存 安全 Java
Java并发编程进阶:深入理解Java内存模型
【4月更文挑战第6天】Java内存模型(JMM)是多线程编程的关键,定义了线程间共享变量读写的规则,确保数据一致性和可见性。主要包括原子性、可见性和有序性三大特性。Happens-Before原则规定操作顺序,内存屏障和锁则保障这些原则的实施。理解JMM和相关机制对于编写线程安全、高性能的Java并发程序至关重要。
|
1天前
|
存储 机器学习/深度学习 Java
【Java探索之旅】数组使用 初探JVM内存布局
【Java探索之旅】数组使用 初探JVM内存布局
9 0
|
2天前
|
安全 Java 编译器
Java面向对象思想以及原理以及内存图解(下)
Java面向对象思想以及原理以及内存图解(下)
10 0
|
2天前
|
Java
Java面向对象思想以及原理以及内存图解(上)
Java面向对象思想以及原理以及内存图解
10 0