Runtime Data Area 运行时数据区就是我们常说的jvm内存
1.程序计数器
类似汇编语言中的cs:ip,指向下条指令地址。多线程中每个线程都有自己的程序计数器
2.java栈
java栈也称作虚拟机栈,事实上java栈是java方法执行的内存模型。
java栈中存放许多栈帧,每个栈帧对应一个被调用的方法。当线程执行一个方法就会创建一个对应的栈帧,并将之压栈,方法执行完之后将
栈帧出栈。当然每个线程都有对应的虚拟机栈。
栈帧中包含局部变量表、操作数栈、指向当前方法所属类的运行时常量池的引用、方法返回地址和一些额外信息。
①局部变量表(local variables):用于存储方法中的局部变量(包括在方法中声明的非静态变量以及函数形参)。对于基本数据类型变量,直接存储值,而引用类型的变量,则存的是指向对象的引用。
局部变量表所需的内存空间在编译期间完成分配,大小是确定的其中64位的long和double占两个局部变量空间,其余占一个。在方法运行期间表大小了两种异常:如果线程请求的栈深度大于虚拟机允许的深度,将抛出StackOverflowError异常;如果虚拟机可以动态扩展,当扩展时无法申请足够内存会抛出OutOfMemoryError异常
②操作数栈(operand stack):“详情了解栈的应用之表达式求解”
③运行时常量池的引用(reference to runtim是不会改变的。
java虚拟机规范,对这个区域规定e constant pool):方法执行中可能要用到类中的常量,需要一个引用指向他们
④方法返回地址(return address):一个方法执行之后,要返回之前调用它的地方,就是这个返回地址
3.本地方法栈
与java栈的作用和原理非常相似。本地方法栈是为执行本地方法服务的。
4.堆heap
堆是所有线程共享的一块内存区域,在虚拟机启动时创建。所有的对象实例和数组都要在堆上分配内存,这也是它的唯一目的。
堆可以只要逻辑上连续就行。如果在堆中没有内存完成实例分配,并且堆也无法扩展时,将会抛出OutOfMemoryError异常。
5.方法区
方法区又叫静态区,跟堆一样,被所有线程共享。方法区中包含的是整个程序中永远唯一的元素,如class,static变量
现给出例子以更直观了解:
public class AppMain //运行时, 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();
}
}
public class Sample //运行时, jvm 把appmain的信息都放入方法区
{
private name; //new Sample实例后, name 引用放入栈区里, name 对象放入堆里
public Sample(String name)
{
this .name = name;
}
public void printName() //print方法本身放入 方法区里。
{
System.out.println(name);
}
}
系统启动java虚拟机进程后,首先从classpath中找到AppMain.class文件,读取这个文件中的二进制数据,然后把AppMain类的类信息存放到运行时数据区的方法区中。即AppMain类的加载。
接着,java虚拟机定位到方法区中AppMain类的Main()方法的字节码,开始执行它的指令。第一句:
Sample test1 = new Sample("测试1");
1.虚拟机建一个Sample实例,到方法区找Sample类的类型信息。没有,加载Sample类。
2.在堆中为Sample实例分配内存,这个Sample实例持有指向方法区中Sample类的类型信息的引用。这个引用实际上就是Sample类类型信息在方法区中的内存地址。
3.test1就是在方法栈中的的一个变量。“=”将这个test1变量指向堆区中的Sample实例。
接下来,虚拟机执行test1.printName()方法时,根据局部变量test1持有的引用,定位到堆中的Sample实例,再根据Sample实例持有的引用,定位到方法区中Sample类的类型信息,从而获得printName()方法的字节码,执行printName()方法包含的指令。