一、Java运行时数据区
Java管理的内存包括以下几个运行时数据区:
1. 程序计数器(Program Counter Register)
是一块较小的内存空间,作用可以看作当前线程所执行的字节码的行号指示数,为线程私有。
如果线程执行的是一个Native方法,这个计数器值为空。
此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError的区域。
2. Java虚拟机栈
线程私有,描述的是Java方法执行内存模型:每个方法被执行的时候会同时创建一个帧栈用于存储局部变量表、操作栈、动态链接、方法出口等信息。
局部变量表存放了编译期可知的基本数据类型和对象引用、return Address类型。
如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StockOverFlowError异常。
3. 本地方法栈
与虚拟机栈所发挥的作用非常相似,区别是本地方法栈为虚拟机使用到的Native方法服务。
4. Java堆
所有线程共享的一块内存区域,唯一的目的是存放对象实例。
Java堆可以细分为新生代和老生代,新生代包括Eden空间、From Survivor空间和ToSurvivor。
Java堆大小由-Xms、-Xmx参数来设定。
5.方法区
也叫Permanent Generation,各线程共享的内存区域,用于存放被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等。
这个区域也会进行垃圾回收,回收的目的是针对常量池的回收和类型的卸载。
运行时常量池是方法区的一部分。(经测试,两个类中的成员同一个字符串常量,将这两个类中的成员用“==”比较,返回值为true。由此得出,常量池并不被类私有。)
方法区大小由-XX:PermSize、-XX:MaxPermSize参数来设定。
6.直接内存
NIO类引入了基于通道和缓冲区的I/O方式,引用Native函数直接分配堆外内存,然后通过存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。
直接内存大小由-XX:MaxDirectMemorySize参数来设定,默认与Java 堆最大值(-Xms)一样。
Java运行时数据区如图所示:
二、对象访问方式
主流的访问方式有两种:使用句柄和直接指针
1. 使用句柄的访问方式,Java堆中分划分出一块内存作为句柄池,refrence中存放的是句柄的地址,句柄中包含了对象实例和类型数据各自的地址。
优势:对象被移动时,只会改变句柄中的实例数据指针,reference本身不需要修改。
2.直接指针方式,reference中存放对象地址,Java堆中的对象需要考虑如何访问类型数据。
优势:速度快,节省了一次指针定位开销。
Sun HotSpot虚拟机采用直接指针方式。