1. 区域划分
线程私有:程序计数器、虚拟机栈、本地方法栈
线程共享:堆、方法区
1.2 程序计数器(Program Counter Register)
程序计数器:记录当前线程所执行的字节码的行号,应该类似于PC指针,字节码解释器根据计数器的值来选取要执行的字节码。
每个线程都有一个独立的计数器,可以保证线程切换后还能够恢复到切换前的执行位置。
1.3 Java虚拟机栈(Java Virtual Machine Stacks)
虚拟机栈:描述的是Java方法执行的内存模型:每个方法被执行时会创建一个栈帧(Stack Frame),存储局部变量表、操作栈、动态链接、方法出口等信息。每个方法从创建到执行完成,就对应该方法的栈帧在虚拟机栈中入栈出栈的过程。
通常概念中的栈指的就是虚拟机栈或其中的局部变量表。存放的是编译器可知的各种基本数据类型、对象引用(注意不是对象)和return Address类型(方法执行完返回后要执行的下一条字节码指令的地址)。
1.4 本地方法栈(Native Method Stacks)
本地方法栈:本地方法即Java调用非Java代码的接口,本地方法栈功能与虚拟机栈类似,只不过是为本地方法服务的。
1.5 Java堆(Java Heap)
存放对象实例,是垃圾收集器主要管理的区域。
1.6 方法区(Method Area)
存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。其中存放编译器生成的各种字面量和符号引用的区域叫做运行时常量池。
JDK1.7及之后版本的 JVM 已经将运行时常量池从方法区中移了出来,在 Java 堆(Heap)中开辟了一块区域存放运行时常量池。
2. 对象访问
新建一个对象并对使用一个对象引用进行访问,需要存放对象引用、对象实例和类相关信息和方法的区域,对应了Java栈、Java堆和方法区三个区域。
Object obj = new Object()
对于上面这条语句,obj作为一个对象引用,被存放在栈中,新建的对象实例的各个字段的数据被存放在了堆中,而Object类的类型数据(对象类型、父类、实现的接口、方法等)存放在方法区中。
而引用类型定位对象的实现有两种:句柄和直接指针。
2.1 句柄访问
Java堆中有一块区域作为句柄池,reference中存放的是句柄地址,句柄中存放的是对象实例数据和类型数据各自的地址,即引用类型无法直接寻找到对象,要通过句柄转换。如果对象被移动了,只需要更改到对象实例数据的指针即可,不需要修改reference。
2.2 直接指针访问
reference直接指向了Java堆中对象实例数据的区域,而该区域中又存储了指向对象类型数据的指针。直接指针访问不需要句柄访问那样的中间转换过程,对对象的访问更快。
2.3 总结
由于引用的多种实现方法,所以说它和C++的指针只是有相似的地方,实现的方法并不一样,而且Java中存在内存的动态分配和回收,所以也不存在C++中的解引用的操作。
3 参考
《深入理解Java虚拟机:JVM高级特性与最佳实践》