Java运行时数据区
1.程序计数器:当前线程所执行的字节码行号的指示器。
java虚拟机多线程是通过线程间轮流切换来分配给处理器执行时间;在确定时间节点,一个处理器(一核)只会执行一个线程的指令;为保证 线程切换 回来后能恢复到原执行位置,各个线程间计数器互相不影响,独立存储
如果程序执行的是一个Java方法,则计数器记录的是正在执行的虚拟机字节码指令地址;如果正在执行的是一个本地(native)方法,则计数器的值为Undefined,由于程序计数器只是记录当前指令地址,所以不存在内存溢出的情况,因此,程序计数器也是所有JVM内存区域中唯一一个没有定义OutOfMemoryError的区域。
2.栈:Java方法执行的内存模型。
Java栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法。当方法被调用时,栈帧在JVM栈中入栈,当方法执行完成时,栈帧出栈
栈帧中包括局部变量表、操作数栈、指向当前方法所属类的运行时常量池的引用、方法返回地址和一些额外的附加信息。
局部变量表:就是用来存储方法中的局部变量(包括在方法中声明的非静态变量以及方法形参)。对于基本数据类型的变量,则直接存储它的值,对于引用类型的变量,则存的是指向对象的引用。局部变量表的大小在编译器就可以确定其大小了,因此在程序执行期间局部变量表的大小是不会改变的。
操作数栈:最典型的一个应用就是用来对表达式求值。想想一个线程执行方法的过程中,实际上就是不断执行语句的过程,归根到底就是进行计算的过程。因此可以这么说,程序中的所有计算过程都是在借助于操作数栈来完成的。
指向运行时常量池的引用: 因为在方法执行的过程中有可能需要用到类中的常量,所以必须要有一个引用指向运行时常量池。
方法返回地址: 当一个方法执行完毕之后,要返回之前调用它的地方,因此在栈帧中必须保存一个方法返回地址。
3.本地方法:栈是执行Java方法的,而本地方法栈是用来执行native方法的
4.堆:主要用于存放对象实例,几乎所有的对象实例都在这里分配内存,是垃圾收集器管理的主要区域
5.方法区:主要用于存储已被虚拟机加载的类信息(包括类的名称、方法信息、字段信息)、常量、静态变量、编译器编译后的代码等.
在方法区中存在一个叫运行时常量池的区域,它主要用于存放编译器生成的各种字面量和符号引用、翻译出来的直接引用(符号引用就是编码是用字符串表示某个变量、接口的位置,直接引用就是根据符号引用翻译出来的地址,将在类链接阶段完成翻译);这些内容将在类加载后存放到运行时常量池中,以便后续使用。它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。当然并非Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,比如String的intern方法。
一个指向A的class对象,一个指向加载自己的classLoader
栈是编译时分配空间,而堆是动态分配(运行时分配空间),所以栈的速度快cpu有专门的寄存器(esp,ebp)来操作栈,堆都是使用间接寻址的。
栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共享。堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。