什么是java OOM?如何分析及解决oom问题?【转载】

转载自:https://www.cnblogs.com/ThinkVenus/p/6805495.htmlhtml

最近查找了不少关于OOM,甚至于Java内存管理以及JVM的相关资料,发现这方面的东西太多了,竟有一种眼花缭乱的感受,要想了解全面的话,恐非一篇文章能说清的,所以按照本身的理解整理了一篇,剩下的还须要继续学习。java

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,每每是逻辑上的缘由泄露。
 
3)OOM的类型
 
JVM内存模型:
 
按照JVM规范,JAVA虚拟机在运行时会管理如下的内存区域:
  • 程序计数器:当前线程执行的字节码的行号指示器,线程私有
  • JAVA虚拟机栈:Java方法执行的内存模型,每一个Java方法的执行对应着一个栈帧的进栈和出栈的操做。
  • 本地方法栈:相似“ JAVA虚拟机栈 ”,可是为native方法的运行提供内存环境。
  • JAVA堆:对象内存分配的地方,内存垃圾回收的主要区域,全部线程共享。可分为新生代,老生代。
  • 方法区:用于存储已经被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。Hotspot中的“永久代”。
  • 运行时常量池:方法区的一部分,存储常量信息,如各类字面量、符号引用等。
  • 直接内存:并非JVM运行时数据区的一部分, 可直接访问的内存, 好比NIO会用到这部分。
按照JVM规范,除了程序计数器不会抛出OOM外,其余各个内存区域均可能会抛出OOM。
 
最多见的OOM状况有如下三种:
  • java.lang.OutOfMemoryError: Java heap space ------>java堆内存溢出,此种状况最多见,通常因为内存泄露或者堆的大小设置不当引发。对于内存泄露,须要经过内存监控软件查找程序中的泄露代码,而堆大小能够经过虚拟机参数-Xms,-Xmx等修改。
  • java.lang.OutOfMemoryError: PermGen space ------>java永久代溢出,即方法区溢出了,通常出现于大量Class或者jsp页面,或者采用cglib等反射机制的状况,由于上述状况会产生大量的Class信息存储于方法区。此种状况能够经过更改方法区的大小来解决,使用相似-XX:PermSize=64m -XX:MaxPermSize=256m的形式修改。另外,过多的常量尤为是字符串也会致使方法区溢出。
  • java.lang.StackOverflowError ------> 不会抛OOM error,但也是比较常见的Java内存溢出。JAVA虚拟机栈溢出,通常是因为程序中存在死循环或者深度递归调用形成的,栈大小设置过小也会出现此种溢出。能够经过虚拟机参数-Xss来设置栈的大小。
4) OOM分析--heapdump
 
要dump堆的内存镜像,能够采用以下两种方式:
  • 设置JVM参数-XX:+HeapDumpOnOutOfMemoryError,设定当发生OOM时自动dump出堆信息。不过该方法须要JDK5以上版本。
  • 使用JDK自带的jmap命令。"jmap -dump:format=b,file=heap.bin <pid>"   其中pid能够经过jps获取。
dump堆内存信息后,须要对dump出的文件进行分析,从而找到OOM的缘由。经常使用的工具备:
  • mat: eclipse memory analyzer, 基于eclipse RCP的内存分析工具。详细信息参见:http://www.eclipse.org/mat/,推荐使用。   
  • jhat:JDK自带的java heap analyze tool,能够将堆中的对象以html的形式显示出来,包括对象的数量,大小等等,并支持对象查询语言OQL,分析相关的应用后,能够经过http://localhost:7000来访问分析结果。不推荐使用,由于在实际的排查过程当中,通常是先在生产环境 dump出文件来,而后拉到本身的开发机器上分析,因此,不如采用高级的分析工具好比前面的mat来的高效。
这个连接:http://www.ibm.com/developerworks/cn/opensource/os-cn-ecl-ma/index.html中提供了一个采用mat分析的例子 。
 
注意:由于JVM规范没有对dump出的文件的格式进行定义,因此不一样的虚拟机产生的dump文件并非同样的。在分析时,须要针对不一样的虚拟机的输出采用不一样的分析工具(固然,有的工具能够兼容多个虚拟机的格式)。IBM HeapAnalyzer也是分析heap的一个经常使用的工具。
 
5)小结
 
涉及到的虚拟机的技术或者工具,每每须要考虑到虚拟机规范以及不一样的虚拟机实现。尤为是针对虚拟机调优时,每每须要针对虚拟机在某些方面的实现策略来考虑,好比,不一样的虚拟机的垃圾回收算法是不同的,而这直接影响了虚拟机某些参数的设置,以达到虚拟机的最佳性能。
而针对JVM运行时的分析与诊断,则须要掌握分析基本方法,针对具体状况,运用虚拟机的原理,具体分析。一句话,水很深啊。
相关文章
相关标签/搜索