在读《深入理解Java虚拟机》一书中,对整个jvm体系有了一个深入理解。本文对Java内存区域与内存溢出异常做一个知识总结。
运行时数据区:
java虚拟机管理的内存会分为:
线程私有:程序计数器,虚拟机栈,本地方法栈,程序计数器,
线程共享:方法区,堆。
程序计数器(Program Counter Register):当前线程所执行的字节码的行号指示器,通过程序计数器来获取下一条指令。(之所以这部分是线程私有的原因是:在线程切换后能恢复到原来正确的执行位置,每个线程都必须有一个程序计数器,各个程序计数器不能互不影响);
内存溢出异常:在《Java虚拟机规范》中,此区域是唯一没有内存溢出异常的区域。
虚拟机栈(VM Stack):Java方法所执行的内存模型(内存模型:每个方法所执行时,都会在虚拟栈内创建一个栈帧,方法执行完毕,对应方法的出栈);
内存溢出异常:如果线程请求的栈深度大于虚拟机栈所允许的最大栈深度,抛出(StackOverFlowError);如果虚拟机栈允许动态扩展,当扩展到无法申请到足够的内存则抛出(OutOfMemoryError);
本地方法栈(Native Method Stacks):与虚拟机栈基本无区别,本地方法栈使用到的是java的本地方法(由c/c++/汇编语言编写);
内存溢出异常:与虚拟机栈一样–》如果线程请求的栈深度大于虚拟机栈所允许的最大栈深度,抛出(StackOverFlowError);如果虚拟机栈允许动态扩展,当扩展到无法申请到足够的内存则抛出(OutOfMemoryError);
Java堆(Java Heap):Java堆是线程共享的一块内存区域,此内存区域的唯一目的就是存放对象实例。
内存溢出异常:目前主流的java虚拟机都是允许堆可以动态扩展,当堆内存大小无法申请足够的内存抛出(OutOfMemoryError),因为堆中存放的是对象,当我们一直创建对象,当对象的回收远不及创建的速度,即抛出此异常)。
方法区(Method Area):用于存储被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据;需要注意的是《Java虚拟机规范》中提到此区域可以不实现垃圾回收,此区域的垃圾回收主要是常量池的回收,但是大部分的常量不会被回收,在早期的虚拟机中,此区域经常出bug,导致内存泄漏。
内存溢出异常:方法区无法满足新的内存分配需求时,将抛出OOM;
运行时常量池(Runtime Constant Pool):此区域是方法区的一部分,Class文件编译期生成的各种字面量和符号引用,会在类加载后放到方法区。(但常量并不都是在编译期生成的,常量池具有动态性,可以在程序运行时产生新的常量);
内存溢出异常:因为此区域是方法区的一部分,受到方法区的限制,当常量池无法再申请到内存时会抛出OOM的异常。
《深入理解java虚拟机》这本书让我对jvm有了一个较深入的理解,每次读都有新的收获,建议大家深入的去读一下。