深入理解Java虚拟机笔记--JVM内存模型及溢出问题总结

来源:互联网 发布:java class加密工具 编辑:程序博客网 时间:2024/05/01 11:13

常见内存溢出类型:

java.lang.OutOfMemoryError: PermGen space

StackOverflowError

java.lang.OutOfMemoryError: unable to create new native thread

java.lang.OutOfMemoryError: Java heap space


JVM运行期内存模型:包括方法区(Perm Gen,永久代), 虚拟机栈,本地方法栈,堆,程序计数器。

方法区与堆是所有线程共享,其他几个是线程私有。一般关心的是虚拟机栈和堆。

1) 方法区(Perm Gen)

方法区用于存放虚拟机加载的类信息,常量,静态变量等数据。当方法区无法满足内存分配要求时,将抛出java.lang.OutOfMemoryError: PermGen space 异常。GC(Garbage Collection)一般不会在主程序运行期对PermGen space进行清理,如果应用中有很多CLASS的话,就很可能出现PermGen space错误,这种错误常见在web服务器对JSP进行pre compile的时候。

另外,程序中尽量不要写占用内存很大的静态变量或者常量,也有可能将该区域挤爆。

可以通过设置JVM的参数:PermSize与MaxPermSize来调整大小。例如:

-XX:PermSize=256M -XX:MaxPermSize=512m
Win 32 JDK 1.6 HotSpot MaxPermSize默认64M

2)虚拟机栈,本地方法栈

虚拟机栈生命周期与线程相同。虚拟机栈描述的是JAVA方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧,用于存储局部变量表等信息。这个区域有两种异常:如果栈深度大于虚拟机所允许的深度,比如做深度递归或者某个线程的本地变量表过大,将抛出StackOverflowError的异常;在多线程环境下,当创建一个新的线程时会分配栈空间,如果没有足够的内存时会抛出OutOfMemoryError: unable to create new native thread异常。

本地方法栈与虚拟机栈类似,虚拟机栈为虚拟机执行Java方法服务,本地方法栈则是为虚拟机使用的Native方法服务。有的虚拟机比如HotSpot将他们二者合二为一。

虚拟机栈与本地方法栈需要的内存并不是虚拟机分配的内存,而是直接从主机内存获取。通过JVM可以控制的参数是Java堆和方法区这两部分的内存。所有 32 位应用程序都有 4 GB 的进程地址空间(32 位地址最多可以映射 4 GB 的内存)。以windows 32位系统为例,应用程序可以访问 2 GB 的进程地址空间,称为用户模式虚拟地址空间。应用程序拥有的所有线程都共享同一个用户模式虚拟地址空间。其余 2 GB 为操作系统保留(也称为内核模式地址空间)。因此java进程最多占有2G内存,再减去JVM申请的内存(Xmx最大堆容量+MaxPermSize最大方法区容量,程序计数器内存很小,可忽略),如果不考虑虚拟机进程本身耗费的内存,那么剩下的内存就是虚拟机栈和本地方法栈的最大可用内存。每个线程分配的栈容量越大,可建立线程的数量就越少。

可以通过设置JVM的参数:-Xss参数设置每个线程的栈容量. JDK1.5以后的默认值是1M.


3) 堆

应用最频繁的区域,也是内存溢出最常见的区域。在虚拟机启动时创建,此区域的唯一目的就是存放对象实例。堆是GC的主要区域,可以细分为新生代(Yong generation)和老年代(Tenured generation)。新生代又可以细分为Eden空间,From Survivor空间,To Survivor空间。HotSpot虚拟机默认Eden和Survivor的大小比例是8:1。另外,线程共享的Java堆中可能划分出多个线程私有的分配缓冲器(Thread Local Allocation Buffer, TLAB)。

一般情况下,对象实例在Eden区域分配,当该区域没有足够空间进行分配时,JVM触发Minor GC,将存活的对象按照Survivor->Tenured generation方向转移。

通过参数-XX:+HeapDumpOnOutOfMemoryError和-XX:HeapDumpPath可以在虚拟机出现内存溢出时Dump当前的内存堆转储快照进行离线分析(可以用visualvm等可视化工具)。例如:

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\dump,将在内存溢出时在D盘下生成名为dump的堆转储文件。
分析确定是否存在内存泄露,如果不存在,可以通过参数调节堆的大小:-Xmx与-Xms,将两者设为同一个值可以避免堆自动扩展。


4)程序计数器

唯一一个在JVM规范中没有规定任何OOM的区域。



参考文献:

深入理解Java虚拟机:JVM高级特性与最佳实践

0 0