JAVA内存区域与内存溢出异常(参考书籍:深入理解Java虚拟机 周志明 著 机械工业出版社)
概述
Java与C/C程序不同,Java开发人员不需要考虑每一个被new的对象的delete/free问题,这些问题交给Java虚拟机来管理,因此,一旦出现了内存的溢出和泄露,如果不了解虚拟机的工作机制就需要无法排查错误。
1.运行时的数据区域
数据区可以分为四大块:方法区、堆、栈(包括虚拟机栈和本地方法栈)、程序计数器。下面一一介绍:
图片来源:博客园 作者: fengbs 《JAVA运行时数据区域》
https://www.cnblogs.com/fengbs/p/7029013.html
程序计数器:
1. 线程私有
2. 它是一块较小的内存空间,可以看做行号指示器,也就是说它作用是取下一条执行的字节码指令(如分支循环跳转异常处理线程恢复等等)。每一条线程对应一个独立的程序计数器,因此各个线程计数器之间互不影响独立存储,因此它是“线程私有的”。
3. 执行Java方法:计数器记录正在执行的虚拟机字节码指令的地址
执行Native方法:计数器值为Undefined
4. 因此,它是唯一一个没有规定任何OutOfMemoryError的区域
Java虚拟机栈:
1. 线程私有
2. 他描述的是Java方法执行的内存模型,每一个方法执行的时候会创建一个栈帧(Stack frame),它存储局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法从调用直到执行,就有一个栈帧在虚拟机中入栈和出栈。
3. 局部变量表:存储编译期间可知的各种基本数据类型(boolean、byte、char、long、double)和对象引用(reference类型,对象的引用指针)类型。个人认为也就是存储JAVA类代码“上面”的数据声明,暂时不知道对不对,日后再说。其中double和long占用两个局部变量空间(Slot),其余的数据类型只占用一个。这个表需要多大内存呢?这在编译期间决定。因此一个方法进入时,分配多大的局部变量空间是确定的。
4. 这个区域有两种异常:如果线程请求的栈深度大于虚拟机允许的深度,抛出StackOverflowError;如果虚拟机支持动态扩展,但是扩展也无法满足请求的内存的时候,会抛出OutOfMemoryError异常
本地方法栈:
与虚拟机栈作用相似,区别在于它为虚拟机使用到的native方法服务,而不是Java方法。
他会抛出StackOverflowError和OutOfMemoryError。
Java堆
这是JVM所管理的内存中最大的一块,被所有线程共享,虚拟机启动时创建。
作用:存放对象实力,所有的对象实例在这里分配内存。
Java堆可以位于物理上不连续的内存,但是需要逻辑上连续。Java堆可以细分为:新生代和老年代,再细致一点可以分为Eden空间、From Survivor空间、To Survivor空间等。但是他还可以划分出某些多个线程私有的分配缓冲区。这是垃圾收集器管理的主要区域,因此JAVA堆又被叫做GC堆(Garbage Collected Heap),具体在下一章详细介绍
方法区
它也是所有线程共享的内存区域,虽然JVM规范把它描述为堆的逻辑部分,但是它的名字叫做Non-heap,与Java堆区分。
作用:存放被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码。
和Java堆一样,可以可以位于物理上不连续的内存,但是需要逻辑上连续。这部分的垃圾回收针对常量池回收和类型的卸载,条件相当苛刻,但这是必要的。当它的内存分配需求没有得到满足的时候会抛出OutOfMemoryError异常。
运行时常量池(Runtime Constant Pool)
这是方法区的一部分,因此也会抛出OutOfMemoryError.
Class文件除了有类的版本、字段、方法、接口等描述信息外,还有一项信息便是常量池(Constant Pool table),它用来存放编译器生成的各种字面量和符号的引用,它们在类被加载后进入方法区的运行时常量池中存放
JVM规范对Class文件的每一部分的格式都有严格的规定,但是唯独对运行时常量池没有做任何细节的要求,不同的虚拟机可以按照自己的实际需求来实现这个内存区域。
它具备动态性,常量不仅可以在编译期间通过方法区进入运行时常量池,运行期间也可以把新的常量放进池中,很常见的一个便是String类的intern()方法。
直接内存(Direct Memory)
注意!!这不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,他也会抛出OutOfMemoryError。
它的内存分配不会受到Java堆的大小限制,但是他会受到本机总内存包括Ram和Swap区或者分页大小的限制。服务器管理员在分配虚拟机参数的时候容易忽略直接内存,是的各个内存区域总和大于无力内存限制,从而导致动态扩展的时候出现OutOfMemoryError。
HotSpot虚拟机与对象
现在我们大致知道了JVM中内存大概有哪些东西以及分别是干什么的。下面以常用虚拟机HotSpot和常用内存区域Java堆为例,看看如何在Java堆中分配、布局、访问一个对象