线程私有内存:程序计数器,Java虚拟机栈,本地方法栈
线程共享内存:Java堆,方法区
引用
程序计数器(Program Counter Register):当前线程所执行的字节码的行号指示器。如果线程执行的是一个java方法,这个计算器记录的是正在持行的虚拟机字节码指令的地址;如果执行的是Native方法,这个计数器则为空。此区域是java虚拟器规范中唯一没有定义任何OutOfMemoryError情况的区域
引用
Java虚拟机栈:它是描述java方法执行的内存模型:每个方法被执行的时候都会创建一个stack frame用于储存局部变量表,操作栈,动态链接,方法出口等信息。每一个方法被调用至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈道出栈的过程。
如果线程所请求的栈深度大于虚拟机所永许的深度,将抛出StackOverflowError,如果虚拟机栈可以动态扩展,当扩展时无法申请足够的内存时会抛出OutOfMemoryError。
如果线程所请求的栈深度大于虚拟机所永许的深度,将抛出StackOverflowError,如果虚拟机栈可以动态扩展,当扩展时无法申请足够的内存时会抛出OutOfMemoryError。
引用
本地方法栈:与java虚拟机栈作用类似,不过他是位执行Native方法服务。本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError
引用
Java堆: 它是虚拟机管理的内存中最大的一块。它是被所有线程共享的一块区域,是虚拟机创建的。它是存放对象实例,几乎所有对象实例都在堆上分配。它是垃圾回收器管理的主要区域。可以进一步分为Eden空间,from survivor控件爱你,to survivor空间等,可以通过-Xmx 和 -Xms 控制堆的大小,当缺少内存时出现OutOfMemoryError
引用
方法区:它和java堆一样,是各个线程共享的内存区域,它用于存储以被虚拟器加载的类信息/常量/静态变量/即时编译器编译后的代码等数据。如果它无法满足内存分配,将抛出OutOfMemoryError异常。
运行时常量池:它是方法区的一部分,Class文件中除啦有类的版本,字段,方法,接口等描述性信息外,还有一个是常量池(Constant Pool Table),用于存放编译器生成的各种字面量和符号引用,这部分内容会在类加载后存放到方法去的运行时常量池中。当缺少内存时出现OutOfMemoryError
运行时常量池:它是方法区的一部分,Class文件中除啦有类的版本,字段,方法,接口等描述性信息外,还有一个是常量池(Constant Pool Table),用于存放编译器生成的各种字面量和符号引用,这部分内容会在类加载后存放到方法去的运行时常量池中。当缺少内存时出现OutOfMemoryError
2 基于sun jvm 实战OutOfMemoryError异常情况和分析过程
堆内存错误分析
import java.util.ArrayList; import java.util.List; /** *VM Arguments: -verbose:gc -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError */ public class HeapOOM { static class OOMObject{} public static void main(String[] args) { List<OOMObject> list = new ArrayList<OOMObject>(); while(true) { list.add(new OOMObject()); } } }
添加eclipse运行参数 -verbose:gc -Xms20m -Xmx20m
参数含义
-verbose:gc | 显示gc过程 |
-Xms | 堆的最小动态内存 |
-Xmx | 堆的最大动态内存 |
-XX:+HeapDumpOnOutOfMemoryError | dump堆快照 |
运行结果:
引用
可以使用eclipse memory analyzer这个Jvm堆分析内存泄漏或者溢出的原因.
eclipse memory analyzer update url: http://download.eclipse.org/mat/1.1/update-site/
如果内存泄漏可以看卡泄漏对象到GC Roots的引用链,从而分析出泄漏的代码位置。我们可从下图看出OOMObject占用大量堆空间。
如果是内存溢出,那就是说内存中的对象都应该存在着,那就检查虚拟机参数-Xmx与-Xms是否可以变大。
eclipse memory analyzer update url: http://download.eclipse.org/mat/1.1/update-site/
如果内存泄漏可以看卡泄漏对象到GC Roots的引用链,从而分析出泄漏的代码位置。我们可从下图看出OOMObject占用大量堆空间。
如果是内存溢出,那就是说内存中的对象都应该存在着,那就检查虚拟机参数-Xmx与-Xms是否可以变大。
eclipse memory analyzer dominator tree 视图
Shallow heap | 对象自身占用的heap空间,不包括它引用的对象 |
Retained heap | 前对象+当前对象可直接或间接引用到的对象总体所占用的空间 |
其他与jvm内存空间相关参数
-Xss | 设置虚拟机栈和本地方法栈的大小 |
-XX:PermSize | 方法区最小空间 |
-XX:MaxPermSize | 方法区最大空间 |
栈溢出
public class JavaVMStackOOM { private int stackLength = 1; public void stackLeak() { stackLength++; stackLeak(); } /** * vm args -Xss128k */ public static void main(String[] args) { JavaVMStackOOM stackOOM = new JavaVMStackOOM(); stackOOM.stackLeak(); } }
运行时常量池溢出
public class ConstantsPoolOOM { /** * VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M */ public static void main(String[] args) { List<String> list = new ArrayList<String>(); int i = 0; while (true) { list.add(String.valueOf(i++).intern()); } } }
方法区异常
public class MethodAreaOOM { /** * VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M */ public static void main(String[] args) { while (true) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOMObject.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { return proxy.invokeSuper(obj, args); } }); enhancer.create(); } } static class OOMObject { } }
本机直接内存溢出
public class DirectMemoryOOM { private static final int _1MB = 1024 * 1024; /** * VM Args: -Xmx20M -XX:MaxDirectMemorySize=10M * * @throws IllegalAccessException * @throws IllegalArgumentException */ public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException { Field unsafeFiled = Unsafe.class.getDeclaredFields()[0]; unsafeFiled.setAccessible(true); Unsafe unsafe = (Unsafe) unsafeFiled.get(null); while (true) { unsafe.allocateMemory(_1MB); } } }