• OOM说明、场景和问题解决详解


    1. 什么是OOM?

    OOM,全称“Out Of Memory”,翻译成中文就是“内存用完了”,来源于java.lang.OutOfMemoryError。

    看下关于的官方说明: Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector.

    意思就是说,当JVM因为没有足够的内存来为对象分配空间、并且垃圾回收器也已经没有空间可回收时,就会抛出这个error(注:非exception,因为这个问题已经严重到不足以被应用处理)。

    2. 为什么会OOM?

    为什么会没有内存了呢?原因不外乎有两点:

    1)分配的少了:比如虚拟机本身可使用的内存(一般通过启动时的VM参数指定)太少。

    2)应用用的太多,并且用完没释放,浪费了。此时就会造成内存泄露或者内存溢出。

    内存泄露:申请的内存在被使用完后没有释放,导致虚拟机不能再次使用该内存,此时这段内存就泄露了,因为申请该内存的对象不再使用该内存,而该段内存又不能被虚拟机分配给别人用。

    内存溢出:申请的内存超出了JVM能提供的内存大小,此时称之为溢出。

    在之前没有垃圾自动回收的日子里,比如C语言和C++语言,我们必须亲自负责内存的申请与释放操作,如果申请了内存,用完后又忘记了释放,比如C++中的new了但是没有delete,那么就可能造成内存泄露。偶尔的内存泄露可能不会造成问题,而大量的内存泄露可能会导致内存溢出。

    而在Java语言中,由于存在了垃圾自动回收机制,所以,我们一般不用去主动释放不用的对象所占的内存,也就是理论上来说,是不会存在“内存泄露”的。但是,如果编码不当,比如,将某个对象的引用放到了全局的Map中,虽然方法结束了,但是由于垃圾回收器会根据对象的引用情况来回收内存,导致该对象不能被及时的回收。如果该种情况出现次数多了,就会导致内存溢出,比如系统中经常使用的缓存机制。Java中的内存泄露,不同于C++中的忘了delete,往往是逻辑上的原因泄露。

    =================================================================

    2.1 内存溢出和内存泄露的区别:

    1、内存溢出:程序在分配内存的时候没有足够大的空间了。

    2、内存泄漏:程序在申请内存之后,没有办法释放掉内存,它始终占用着内存,即被分配的对象可达但无用。内存泄露一般都是因为内存中有一块很大的对象,但是无法释放。 会导致内存溢出。

    3. OOM的类型

    3.1 按照JVM规范,JAVA虚拟机在运行时会管理以下的内存区域:

    • 程序计数器:当前线程执行的字节码的行号指示器,线程私有

    • JAVA虚拟机栈:Java方法执行的内存模型,每个Java方法的执行对应着一个栈帧的进栈和出栈的操作。

    • 本地方法栈:类似“ JAVA虚拟机栈 ”,但是为native方法的运行提供内存环境。

    • JAVA堆:对象内存分配的地方,内存垃圾回收的主要区域,所有线程共享。可分为新生代,老生代。

    • 方法区:用于存储已经被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。Hotspot中的“永久代”。

    • 运行时常量池:方法区的一部分,存储常量信息,如各种字面量、符号引用等。

    • 直接内存:并不是JVM运行时数据区的一部分, 可直接访问的内存, 比如:NIO会用到这部分。

    按照JVM规范,除了程序计数器不会抛出OOM外,其他各个内存区域都可能会抛出OOM。

    3.2 最常见的OOM情况有以下三种 + 解决办法

    • java.lang.OutOfMemoryError: Java heap space —— > java 堆内存溢出

      • 出现OOM原因:此种情况最常见,一般由于内存泄露或者堆的大小设置不当引起。

      • 解决办法:对于内存泄露,需要通过内存监控软件查找程序中的泄露代码,而堆大小可以通过虚拟机参数-Xms、-Xmx等修改。

      • 举例:循环调用new A() 导致堆溢出
        虚拟机参数:-Xms1M -Xmx1M -XX:+HeapDumpOnOutOfMemoryError,
        解释:将-Xmx和-Xms设置为一样可以避免堆自动扩展。
        -XX:+HeapDumpOnOutOfMemoryError, 解释:可以让虚拟机在出现内存溢出异常时Dump出当前的堆内存转储快照,然后分析Dump文件

         while (true){
                 new A().do2();
         }

    • java.lang.OutOfMemoryError: PermGen space —— > java 永久代溢出 ,即方法区溢出了

      • 出现OOM原因:一般出现于大量Class 或者 jsp页面,或者采用cglib等反射机制的情况,因为上述情况会产生大量的Class信息存储于方法区。另外,过多的常量,尤其是字符串,也会导致方法区溢出。

      • 解决办法:此种情况可以通过更改方法区的大小来解决,使用类似-XX:PermSize=64m -XX:MaxPermSize=256m 的形式修改。

      • 举例:循环调用String.intern()方法来写入常量池,常量池溢出。
        虚拟机参数:-XX:PermSize=10M -XX:MaxPermSize=10M,
        解释:表示JVM初始分配的永久代的容量和最大容量。(永久区内存不足,1.8后都在堆上。方法区=永久代,PermGen space”,即永久代)

      while (true){
        list.add(String.valueOf(i++).intern());
      }

    • java.lang.StackOverflowError ------> 不会抛OOM error,但也是比较常见的Java内存溢出。

      • 出现OOM原因:JAVA虚拟机栈溢出,一般是由于程序中存在死循环或者深度递归调用造成的,栈大小设置太小也会出现此种溢出。

      • 解决办法 :可以通过虚拟机参数 -Xss 来设置栈的大小。

      • 举例:循环调用对象引用的方式实现栈溢出。
        虚拟机参数:-Xss128k,
        解释:设置虚拟机栈的大小为128kn
        在单线程下,无论栈帧太大还是虚拟机栈容量太小,内存无法分配的时候都会抛出以上错误。

      void do2(){
        do2();
      };

  • 相关阅读:
    PHP面向对象编程入门
    PHP错误处理机制
    PHP数组深入
    PHP 表单
    多重背包之单调队列优化理论性总结
    二分查找理论性总结
    大连海事大学第十届程序设计竞赛 题解
    Codeforces Round #603 (Div. 2) E. Editor (线段树维护前缀和最值)
    Educational Codeforces Round 77 (Rated for Div. 2) E. Tournament (DP)
    Leetcode1256 加密数字(手动找规律)
  • 原文地址:https://www.cnblogs.com/47Gamer/p/13036101.html
Copyright © 2020-2023  润新知