• JVM内存溢出异常OOM


    栈溢出 StackOverflowError

    Java 里的 StackOverflowError。抛出这个错误表明应用程序因为深递归导致栈被耗尽了。每当java程序启动一个新的线程时,java虚拟机会为他分配一个栈,java栈以帧为单位保持线程运行状态;当线程调用一个方法是,jvm压入一个新的栈帧到这个线程的栈中,只要这个方法还没返回,这个栈帧就存在。 如果方法的嵌套调用层次太多(如递归调用),随着java栈中的帧的增多,最终导致这个线程的栈中的所有栈帧的大小的总和大于-Xss设置的值,而产生生StackOverflowError溢出异常。
    StackOverflowError 是 VirtualMachineError 的扩展类,VirtualMachineError 表明 JVM 中断或者已经耗尽资源,无法运行。而且,VirtualMachineError类扩展自 Error 类,这个类用于指出那些应用程序不需捕获的严重问题。因为这些错误是在可能永远不会发生的异常情况下产生,所以方法中没有在它的 throw 语句中声明。
    最常见的可能耗光 Java 应用程序的栈的场景是程序里的递归。递归时一个方法在执行过程中会调用自己。 递归被认为是一个强大的多用途编程技术,为了避免出现 StackOverflowError,使用时必须特别小心。

    public class StackErrorMock {
    
        private static int index = 1;
         
        public void call(){
            index++;
            call();
        }
     
        public static void main(String[] args) {
            StackErrorMock mock = new StackErrorMock();
            try {
                mock.call();
            }catch (Throwable e){
                System.out.println("Stack deep : "+index);
                e.printStackTrace();
            }
        }
    }

    catch 捕获的是 Throwable,而不是 Exception。因为 StackOverflowError 和 OutOfMemoryError 都不属于 Exception 的子类。

    在-Xss100M时
    Stack deep : 6507194
    java.lang.StackOverflowError
    at StackErrorMock.call(StackErrorMock.java:8)

    在-Xss1M时
    Stack deep : 21967
    java.lang.StackOverflowError
    at StackErrorMock.call(StackErrorMock.java:8)

    不配置时
    Stack deep : 22106
    java.lang.StackOverflowError
    at StackErrorMock.call(StackErrorMock.java:8)

    依赖于安装的 Java 虚拟机,默认的线程栈大小可能是 512KB 或者 1MB。你可以使用 -Xss 标识来增加线程栈的大小。这个标识即可以通过项目的配置也可以通过命令行来指定。-Xss 参数的格式:

    -Xss<size>[g|G|m|M|k|K]

    堆溢出 OutOfMemoryError

    import java.util.ArrayList;
    import java.util.List;
    
    public class HeapOverflow {
        public static void main(String[] args) {
            List<Object> listObj = new ArrayList<Object>();
            for(int i=0; i<10; i++){
                Byte[] bytes = new Byte[1024*1024];
                listObj.add(bytes);
            }
            System.out.println("已添加");
        }
    }

    设置参数

    -Xms1m -Xmx5m -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError

    根据以下的信息,可以看到,年轻代使用1536k,老年代4096(对象空间object space 4096K),其他Metaspace 2672K。

    [GC (Allocation Failure) [PSYoungGen: 510K->504K(1024K)] 510K->520K(1536K), 0.0190917 secs] [Times: user=0.00 sys=0.00, real=0.02 secs] 
    [GC (Allocation Failure) [PSYoungGen: 752K->488K(1536K)] 768K->584K(5632K), 0.0009955 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [GC (Allocation Failure) [PSYoungGen: 488K->504K(1536K)] 584K->608K(5632K), 0.0007268 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [Full GC (Allocation Failure) [PSYoungGen: 504K->0K(1536K)] [ParOldGen: 104K->534K(2048K)] 608K->534K(3584K), [Metaspace: 2640K->2640K(1056768K)], 0.0153461 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
    [GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] 534K->534K(5632K), 0.0003560 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] [ParOldGen: 534K->522K(3072K)] 534K->522K(4608K), [Metaspace: 2640K->2640K(1056768K)], 0.0065765 secs] [Times: user=0.06 sys=0.00, real=0.01 secs] 
    java.lang.OutOfMemoryError: Java heap space
    Dumping heap to java_pid8172.hprof ...
    Heap dump file created [1232301 bytes in 0.008 secs]
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at HeadOverflow.main(HeadOverflow.java:8)
    Heap
    PSYoungGen total 1536K, used 41K [0x00000000ffe00000, 0x0000000100000000, 0x0000000100000000)
    eden space 1024K, 4% used [0x00000000ffe00000,0x00000000ffe0a450,0x00000000fff00000)
    from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
    to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
    ParOldGen total 4096K, used 522K [0x00000000ffa00000, 0x00000000ffe00000, 0x00000000ffe00000)
    object space 4096K, 12% used [0x00000000ffa00000,0x00000000ffa828b8,0x00000000ffe00000)
    Metaspace used 2672K, capacity 4486K, committed 4864K, reserved 1056768K
    class space used 291K, capacity 386K, committed 512K, reserved 1048576K

    常量池永久代溢出(java8以前)

    import java.util.ArrayList;
    import java.util.List;
    
    public class RuntimeConstantPoolOOM {
        /*
         * VM Args 1.8以前: -XX:PermSize=1M -XX:MaxPermsize=2M
         * 1.8以后:-XX:MetaspaceSize=1M -XX:MaxMetaspaceSize=2M -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError
         */
     
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            
            //使用list保持常量池的引用,避免被Full GC回收
            List<String> list=new ArrayList<String>();
            int i=0;
            while(true)
            {
                list.add(String.valueOf(i).intern());
            }
     
        }
    
    }

    在 JDK 1.8 中, HotSpot 已经没有 “PermGen space”这个区间了,取而代之是一个叫做 Metaspace(元空间) 的东西。
    可以通过以下参数来指定元空间的大小:

    1.8以前: -XX:PermSize=1M -XX:MaxPermsize=2M
    1.8以后:-XX:MetaspaceSize=1M -XX:MaxMetaspaceSize=2M -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError

      -XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。
      -XX:MaxMetaspaceSize,最大空间,默认是没有限制的。

      除了上面两个指定大小的选项以外,还有两个与 GC 相关的属性:
      -XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
      -XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集

    java.lang.OutOfMemoryError: Metaspace
    Dumping heap to java_pid7928.hprof ...
    Error occurred during initialization of VM
    GC triggered before VM initialization completed. Try increasing NewSize, current value 84M.

    直接内存溢出

    直接内存并不是虚拟机运行时数据区的一部分,也不是Java 虚拟机规范中定义的内存区域。在JDK1.4 中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的 I/O 方式,它可以使用 native 函数库直接分配堆外内存,然后通过一个存储在Java堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆中来回复制数据。

    本机直接内存的分配不会受到Java 堆大小的限制,受到本机总内存大小限制
    直接内存由 -XX:MaxDirectMemorySize 指定
    直接内存申请空间耗费更高的性能
    直接内存IO读写的性能要优于普通的堆内存
    当我们的需要频繁访问大的内存而不是申请和释放空间时,通过使用直接内存可以提高性能。

    由于申请直接内存不由虚拟机管理,所以由此导致的 OOM 是不会在 Heap Dump 文件中看出明显的异常。当 OOM 后发现 Dump 文件很小同时程序直接或间接使用了 NIO ,就可以考虑一下这方面的原因。

    import java.lang.reflect.Field;
    import sun.misc.Unsafe;
    
    public class DirectrMemoryOOM {
        private static final int _1M = 1024 * 1024;
        
        public static void main(String[] args) throws Exception {
            Field unsafeField = Unsafe.class.getDeclaredFields()[0];
            unsafeField.setAccessible(true);
            int index =0 ;
            @SuppressWarnings("restriction")
            Unsafe unsafe = (Unsafe) unsafeField.get(null);
            try {
                while(true) {
                    unsafe.allocateMemory(_1M);
                    index++;
                }
            }catch (Throwable e){
                System.out.println("deep : "+index);
                e.printStackTrace();
            }
            
    
        }
    
    }

    参数

    -Xmx20M -XX:MaxDirectMemorySize=10M

    deep : 51966java.lang.OutOfMemoryError
    at sun.misc.Unsafe.allocateMemory(Native Method)
    at DirectrMemoryOOM.main(DirectrMemoryOOM.java:16)

  • 相关阅读:
    SQL关闭连接,及分离数据库
    C# 用户选择单个压缩-系统自带压缩
    设计模式7大原则--01
    httpclient
    Mac idea 常用命令
    集合整理
    几种简单的排序算法
    基础数据结构简介
    git 常用命令3
    git 常用命令2
  • 原文地址:https://www.cnblogs.com/starcrm/p/13212140.html
Copyright © 2020-2023  润新知