

什么是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,因为这个问题已经严重到不足以被应用处理)。
为什么会OOM?
为什么会没有内存了呢?原因不外乎有两点:
1)分配的少了:比如虚拟机本身可使用的内存(一般通过启动时的VM参数指定)太少。
2)应用用的太多,并且用完没释放,浪费了。此时就会造成内存泄露或者内存溢出。
内存泄露:申请使用完的内存没有释放,导致虚拟机不能再次使用该内存,此时这段内存就泄露了,因为申请者不用了,而又不能被虚拟机分配给别人用。
内存溢出:申请的内存超出了JVM能提供的内存大小,此时称之为溢出。
在之前没有垃圾自动回收的日子里,比如C语言和C++语言,我们必须亲自负责内存的申请与释放操作,如果申请了内存,用完后又忘记了释放,比如C++中的new了但是没有delete,那么就可能造成内存泄露。偶尔的内存泄露可能不会造成问题,而大量的内存泄露可能会导致内存溢出。
而在Java语言中,由于存在了垃圾自动回收机制,所以,我们一般不用去主动释放不用的对象所占的内存,也就是理论上来说,是不会存在“内存泄露”的。但是,如果编码不当,比如,将某个对象的引用放到了全局的Map中,虽然方法结束了,但是由于垃圾回收器会根据对象的引用情况来回收内存,导致该对象不能被及时的回收。如果该种情况出现次数多了,就会导致内存溢出,比如系统中经常使用的缓存机制。Java中的内存泄露,不同于C++中的忘了delete,往往是逻辑上的原因泄露。
OOM的类型
JVM内存模型:
按照JVM规范,JAVA虚拟机在运行时会管理以下的内存区域:
程序计数器:当前线程执行的字节码的行号指示器,线程私有
JAVA虚拟机栈:Java方法执行的内存模型,每个Java方法的执行对应着一个栈帧的进栈和出栈的操作。
本地方法栈:类似“ JAVA虚拟机栈 ”,但是为native方法的运行提供内存环境。
JAVA堆:对象内存分配的地方,内存垃圾回收的主要区域,所有线程共享。可分为新生代,老生代。
方法区:用于存储已经被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。Hotspot中的“永久代”。
运行时常量池:方法区的一部分,存储常量信息,如各种字面量、符号引用等。
直接内存:并不是JVM运行时数据区的一部分, 可直接访问的内存, 比如NIO会用到这部分。
按照JVM规范,除了程序计数器不会抛出OOM外,其他各个内存区域都可能会抛出OOM。
常见的OOM情况

java.lang.OutOfMemoryError:Javaheap space错误(根据实际生产经验,可以对程序日志中的 OutOfMemoryError 配置关键字告警,一经发现,立即处理)。
原因分析
Javaheap space错误产生的常见原因可以分为以下几类:
解决方案
-Xmx参数调高 JVM 堆内存空间即可。如果仍然没有解决,可以参考以下情况做进一步处理:
2、GC overhead limit exceeded
java.lang.OutOfMemoryError:GC overhead limit exceeded错误。简单地说,就是应用程序已经基本耗尽了所有可用内存, GC 也无法回收。
Javaheap space非常类似,可以参考上文。
3、Permgen space
原因分析
解决方案
-XX:MaxPermSize启动参数,调大永久代空间。
-XX:+CMSClassUnloadingEnabled和
-XX:+UseConcMarkSweepGC这两个参数允许 JVM 卸载 class。
jmap-dump:format=b,file=dump.hprof<process-id>,然后利用 Eclipse MAT https://www.eclipse.org/mat 功能逐一分析开销最大的 classloader 和重复 class。
4、Metaspace
Permgenspace非常类似,可以参考上文。需要特别注意的是调整 Metaspace 空间大小的启动参数为
-XX:MaxMetaspaceSize。
5、Unable to create new native thread
原因分析
Unableto createnewnativethread,常见的原因包括以下几类:
java.lang.OutOfMemoryError:Unableto createnewnativethread错误。
解决方案
ulimia-a查看最大线程数限制,使用
ulimit-u xxx调整最大线程数限制。
6、Out of swap space?
Outof swap space?错误。
原因分析
jmap-histo:live<pid>命令,强制执行 Full GC;如果几次执行后内存明显下降,则基本确认为 Direct ByteBuffer 问题。
解决方案
-XX:MaxDirectMemorySize调低阈值。
7、 Kill process or sacrifice child
Killprocessorsacrifice child错误不是由 JVM 层面触发的,而是由操作系统层面触发的。
原因分析
解决方案
8、Requested array size exceeds VM limit
Integer.MAX_VALUE-2。
9、Direct buffer memory
原因分析
Directbuffer memory错误。
解决方案
-XX:MaxDirectMemorySize调整 Direct ByteBuffer 的上限值。
-XX:+DisableExplicitGC选项,如果有就去掉,因为该参数会使
System.gc()失效。
sun.misc.Cleaner的
clean()方法来主动释放被 Direct ByteBuffer 持有的内存空间。




