共计 2797 个字符,预计需要花费 7 分钟才能阅读完成。
导读 | 最近查找了很多关于 OOM,甚至于 Java 内存管理以及 JVM 的相关资料,发现这方面的东西太多了,竟有一种眼花缭乱的感觉,要想了解全面的话,恐非一篇文章能说清的,因此按照自己的理解整理了一篇,剩下的还需要继续学习。 |
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,因为这个问题已经严重到不足以被应用处理)。
为什么会没有内存了呢?原因不外乎有两点:
1)分配的少了:比如虚拟机本身可使用的内存(一般通过启动时的 VM 参数指定)太少。
2)应用用的太多,并且用完没释放,浪费了。此时就会造成内存泄露或者内存溢出。
内存泄露:申请使用完的内存没有释放,导致虚拟机不能再次使用该内存,此时这段内存就泄露了,因为申请者不用了,而又不能被虚拟机分配给别人用。
内存溢出:申请的内存超出了 JVM 能提供的内存大小,此时称之为溢出。
在之前没有垃圾自动回收的日子里,比如 C 语言和 C ++ 语言,我们必须亲自负责内存的申请与释放操作,如果申请了内存,用完后又忘记了释放,比如 C ++ 中的 new 了但是没有 delete,那么就可能造成内存泄露。偶尔的内存泄露可能不会造成问题,而大量的内存泄露可能会导致内存溢出。
而在 Java 语言中,由于存在了垃圾自动回收机制,所以,我们一般不用去主动释放不用的对象所占的内存,也就是理论上来说,是不会存在“内存泄露”的。但是,如果编码不当,比如,将某个对象的引用放到了全局的 Map 中,虽然方法结束了,但是由于垃圾回收器会根据对象的引用情况来回收内存,导致该对象不能被及时的回收。如果该种情况出现次数多了,就会导致内存溢出,比如系统中经常使用的缓存机制。Java 中的内存泄露,不同于 C ++ 中的忘了 delete,往往是逻辑上的原因泄露。
JVM 内存模型:
按照 JVM 规范,JAVA 虚拟机在运行时会管理以下的内存区域:
程序计数器:当前线程执行的字节码的行号指示器,线程私有
JAVA 虚拟机栈:Java 方法执行的内存模型,每个 Java 方法的执行对应着一个栈帧的进栈和出栈的操作。
本地方法栈:类似“JAVA 虚拟机栈”,但是为 native 方法的运行提供内存环境。
JAVA 堆:对象内存分配的地方,内存垃圾回收的主要区域,所有线程共享。可分为新生代,老生代。
方法区:用于存储已经被 JVM 加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。Hotspot 中的“永久代”。
运行时常量池:方法区的一部分,存储常量信息,如各种字面量、符号引用等。
直接内存:并不是 JVM 运行时数据区的一部分,可直接访问的内存,比如 NIO 会用到这部分。
按照 JVM 规范,除了程序计数器不会抛出 OOM 外,其他各个内存区域都可能会抛出 OOM。
最常见的 OOM 情况有以下三种:
要 dump 堆的内存镜像,可以采用如下两种方式:
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 的一个常用的工具。
涉及到的虚拟机的技术或者工具,往往需要考虑到虚拟机规范以及不同的虚拟机实现。尤其是针对虚拟机调优时,往往需要针对虚拟机在某些方面的实现策略来考虑,比如,不同的虚拟机的垃圾回收算法是不一样的,而这直接影响了虚拟机某些参数的设置,以达到虚拟机的最佳性能。