Java内存区域与内存溢出异常

(一)Java虚拟机管理的内存分类

1、方法区(method area):各线程共享的内存区域。用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

(1)别名:非堆(Non-Heap)

(2)特点:不需要连续的内存、可选择固定大小或者可扩展、可以选择不实现垃圾回收(可能导致内存泄漏)

(3)垃圾回收的主要目标:常量池的回收和对类型的卸载

运行时常量池(Runtime Constant Pool):类加载后,class文件常量池进入方法区的运行时常量池中存放。另外,运行期间也可能将新的常量放入池中(如:String的intern方法)

说明:class文件内容:类的版本、字段、接口、方法等描述信息和常量池(用于存放编译期间生成的各种字面变量和符号引用)

Java虚拟机规范中规定的异常:OutOfMemoryError,方法区无法满足内存分配的需求

2、虚拟机栈(VM stack):与线程生命周期相同,是线程私有。描述了Java方法执行的内存模型。为虚拟机执行Java方法(字节码)服务。

对应的栈帧:每个方法执行的时候创建一个栈帧(存储局部变量表、操作数栈、动态链接、方法出口等信息),方法执行开始到完成的过程即为虚拟机栈的一个栈帧从入栈到出栈的过程。

说明:局部变量表中存储了编译器预知的各种基本数据类型、引用类型和returnAddress类型

Java虚拟机规范中规定的异常:

(1)StackOverflow,线程请求的栈深度大于虚拟机所允许的深度(虚拟机栈现在基本都可自动扩展,所以此异常通常不会出现)

(2)OutOfMemoryError,线程请求栈深度在扩展时无法申请到足够的内存

3、本地方法栈(native method stack):线程私有,为虚拟机使用到的native方法服务。虚拟机规范中未对本地方法使用的语言、使用方式与数据结构进行规范,各虚拟机可自由实现。

4、堆(heap):虚拟机启动时创建,为所有线程共享的内存区域。是虚拟机管理内存最大的一块区域,几乎所有(JIT编译器和逃逸分析技术发展,因此不再是绝对的)对象实例和数组都在堆上分配。

堆是垃圾收集器管理的主要区域,因此又被成为”GC堆“。由于垃圾收集器基本采取分代回收算法,因此堆又可细分为新生代和老年代(更细致的话:Eden,Survivor from,Survivior to)。

Java虚拟机规范中规定的异常:如果堆中没有内存完成实例分配,则爬出OutOfmemoryError异常。

5、程序计数器(Program counter register):当前线程私有(确保线程切换后能够回到原本执行的位置)执行的字节码行号指示器

(1)如果当前执行的是Java方法:记录的是正在执行的虚拟机字节码指令的地址

(2)如果当前执行的是native方法:为空

注意:此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError的区域

补充:直接内存

(1)直接内存并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域

(2)JDK1.4引入NIO,是一种基于通道与缓冲区的IO方式。使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存中的引用进行操作,避免对内外数据的复制,提高效率。

本机直接内存与对内存大小无关,但是如果盲目使用,会导致OutOfmemoryerror。

(二)虚拟机内存中数据的具体细节(以虚拟机HotSpot的堆内存为例进行讨论)

1、对象的创建

(1)分配内存

方式:指针碰撞(堆内存规整)、空闲列表(堆内存不规整),根据不同的虚拟机而定

(2)初始化(实例变量的零值)

(3)对象头(属于哪个类,如何找到类的元数据信息,对象的hash码,对象的GC分代年龄信息等)的设置

(4)执行init方法,按照编程者的意愿进行初始化

2、对象的内存布局

(1)对象头

对象hash码、对象分代年龄,指向锁记录的指针,指向重量级锁记录的指针,GC标记,偏向线程ID、偏向时间戳、对象分代年龄。

类型指针,即对象指向他的类元数据的指针,以此确认是哪个类的实例。如果对象是数组,还需要在对象头中指明数组的长度。

(2)实例数据

内容:继承来的和自己的

顺序:虚拟机分配策略(long/double,int,short/chart,byte/boolean,oop)和程序定义顺序共同影响(父类在本身的前面)。另:如果CompactFields参数值为true,则子类较窄的变量会穿插在父类变量缝隙中。

(3)对齐填充:对象大小必须是8字节的整数倍,因此如果没有对齐,则需要进行占位符填充。

3、对象的访问定位

(1)句柄

java堆中分配出一部分空间作为句柄池,引用中存储对象的句柄地址。句柄中包含了对象实例数据和类型数据各自的具体地址信息。

优点:引用存储稳定的句柄地址,不会因为对象移动修改引用地址。(但是句柄中的实例数据指针需要改变)

(2)直接指针

引用中存储的直接就是对象地址。

优点:速度更快,节省了一次指针定位的时间开销

你可能感兴趣的:(Java内存区域与内存溢出异常)