1 java虚拟机内存模型
1.1 程序计数器
如果当前执行的是Java方法,则指示当前字节码指令的地址,如果执行的是本地本地方法,则值为Undefined。每个线程都有自己的程序计数器,也就保证了线程切换的过程中, 该线程执行的程序能恢复到正确的执行位置。
1.2 本地方法栈
一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。本地方栈存放的方法是本地方法,其他的和虚拟机栈基本相同。接下来我们介绍虚拟机栈。
1.3 虚拟机栈
虚拟机栈描述的是Java 方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
我们分析一个方法执行需要哪些信息?
我们需要保存局部变量,对象引用,方法调用,方法返回地址
局部变量表:局部变量和对象引用。局部变量包括各种基本的数据类型。
操作数栈:局部变量表是无法操作变量的,变量要进入操作数栈进行各种操作。就像编译原理中的对指令进行操作的栈。
动态链接:函数调用,我们都知道动态链接过程,就是在方法在运行的时候如果调用了其他的方法,通过动态链接把这两个方法链接到一块。
方法出口:方法的返回地址
1.4 堆
虚拟机栈中局部变量表存放了指向对象的引用,那么这个对象实例具体保存在哪呢?答案是保存在堆中。
1.4.1对象的实例
(1)对象头
(2)对象的实例数据
程序代码中各种所定义的各种类型字段内容。
(3)对齐数据的补充
HotSpot VM自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说,如果不是8字节的整数倍就要填充成8字节的整数倍。
1.5 方法区
栈和堆都是运行时的,那么一些静态信息肯定也要保存,保存这些静态信息的区域就是方法区。
方法区保存虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。运行时常量池也是方法区的一部分。
1.5.1 运行时常量池
每一个Class文件中,都维护着一个常量池(这个保存在类文件里面,不要与方法区的运行时常量池搞混),里面存放着编译时期生成的各种字面值和符号引用;这个常量池的内容,在类加载的时候,被复制到方法区的运行时常量池 ;
字面值:就是像string, 基本数据类型,以及它们的包装类的值,以及final修饰的变量,简单说就是在编译期间,就可以确定下来的值;
符号引用:不同于我们常说的引用,它们是对类型,域和方法的引用,类似于面向过程语言使用的前期绑定,对方法调用产生的引用。例如,在编译时, Java类并不知道所引用的类的实际地址, 因此只能使用符号引用来代替. 比如org.simple.People类引用了org.simple.Language类, 在编译时People类并不知道Language类的实际内存地址, 因此只能使用符号org.simple.Language来表示Language类的地址。
1.5.2 字段信息:字段信息存放类中声明的每一个字段的信息,包括字段的名、类型、修饰符。
1.5.3 方法信息:类中声明的每一个方法的信息,包括方法名、返回值类型、参数类型、修饰符、异常、方法的字节码。
(在编译的时候,就已经将方法的局部变量、操作数栈大小等确定并存放在字节码中,在装载的时候,随着类一起装入方法区。)
1.5.4 静态变量:类变量,类的所有实例都共享,我们只需知道,在方法区有个静态区,静态区专门存放静态变量和静态块。
1.5.5 到类classloader的引用:到该类的类装载器的引用。
1.5.6 到类class 的引用:虚拟机为每一个被装载的类型创建一个class实例,用来代表这个被装载的类。
2 实例说明
package Chapter2;
public class ExampleImpl3
// 运行时, jvm 把appmain的信息都放入方法区
{
public static void main(String[] args) // main 方法本身放入方法区。
{
Sample test1 = new Sample(" 测试1 "); // test1是引用,所以放到栈区里,
// Sample是自定义对象应该放到堆里面
Sample test2 = new Sample(" 测试2 ");
test1.printName(); //运行时,放到虚拟栈
test2.printName();
}
}
class Sample // 运行时, jvm 把appmain的信息都放入方法区
{
private String name; // new Sample实例后, name 引用放入栈区里, name 对象放入堆里
/** 构造方法 */
public Sample(String name)
{
this.name = name;
}
/** 输出 */
public void printName() // print方法本身放入 方法区里。
{
System.out.println(name);
}
}
3 总结:
2.1 静态信息(非运行)
(1)静态的信息放到方法区中。例如类的版本、字段信息、方法信息、到类实例的引用(由此判断实例对象的类型)
2.2 动态信息(运行)
(2)对象的实例放到堆中。例如,对象头,对象实例数据(对象中各种类型的字段的的值)。
(3)方法的运行内存是虚拟机栈。例如,方法中的变量以及对象的引用,操作数栈,方法出口等信息。