参考资料:
1.https://www.cnblogs.com/lipeineng/p/8358601.html
2.https://www.cnblogs.com/dolphin0520/p/3613043.html
3.https://blog.csdn.net/wangxiaotongfan/article/details/82261059
4.https://blog.csdn.net/steady_pace/article/details/51254740
1.Java程序是运行在JVM上的,Java运行过程如下图所示:
java源文件通过java编译器编译为字节码文件,虚拟机的类加载器加载各个类的字节码文件,加载完之后,由Java执行引擎执行,在整个程序执行过程中,虚拟机会分配一段空间用来存储执行期间用到的数据和相关信息,该内存空间被成为Runtime Data Area(运行时数据区),及JVM内存。
关于内存一些名词的解释:
1.PC程序寄存器:记录正在执行虚拟机字节码地址。
2.本地方法栈:和java栈类似,本地方法栈是为native方法准备的。
3.java栈:每个线程中都有一个方法栈,为java方法准备的,栈里存着一个个栈帧,一个栈帧可当作是一个方法的调用。
4.栈帧:局部变量、操作数栈、方法返回地址、和其他信息。
5.操作数栈:和CPU中的ALU联系在一起,是CPU唯一指定的数据来源,执行一条指令,其实是在局部变量表和操作数栈中进行的。
6.方法区:执行方法的内存,java栈中,涉及到类加载,加载进来的类信息、字段、变量、代码等信息,
7.运行时常量池:也是方法区的一部分,存储字面常量、符号引用。
8.符号引用:是字符串,其中有丰富的信息,通过搜索类的方法表,可以定位到类的方法,符号引用只需要第一次引用后,就会被替成直接引用了。
运行时数据区分为:
1.程序寄存器: 一块很小的内存,当前线程所执行字节码行号指示器,字节码解释器工作时就是通过改变这个计数器的值来选取下一条要执行的字节码指令。分支、循环、异常处理、回复线程等基础功能都需要程序寄存器来完成。
特点:
(1)线程私有,由于java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间来实现的,在任意一个时刻,一个处理器只能执行一条线程中的命令,为了线程切换之后能回复到正确执行的位置,每条线程都有独立的程序寄存器,
(2)执行情况说明:线程正在执行一个java方法,这个计数器存储的是现在虚拟机字节码指令的地址;native方法,存的是null。
(3)此内存是jjava虚拟规范唯一一个没有规任何定OOM区域的。
2.java虚拟机栈:是java方法执行的内存模型,每个方法在执行时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息,每个方法从调用到执行完对应着一个栈帧从入栈到出栈的过程。
在java栈中主要保存的栈帧,每次调用函数,都会有一个栈帧被压入java栈,每一个函数调用结束后,该栈帧弹出,当函数中存在调用其他函数,例如函数1调用了函数2,函数2调用了函数3,函数1先入栈,创建对应栈帧,调用函数2,创建相应栈帧,3类似,正在执行的方法位于栈顶,它保存着当前函数的局部白能量和中间值。当函数返回时,栈帧从java栈中依次被弹出,(函数3、函数2、函数1),不管是return返回还是抛异常返回。
如果请求栈深度超出最大可用栈深度,系统会抛出stackoutflowerror栈溢出错误。
栈帧:包括局部变量表、操作数栈、指向当前方法所属类的运行时常量池的引用,方法返回地址和一个附加信息。当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完之后,会将栈帧出栈,由此可知当前方法对应的栈帧位于Java栈帧顶部。这也是为什么使用递归方法时候会内存溢出以及栈区的空间不用程序员管理。
局部变量表:用来存储方法中的局部变量(包括方法声明的非静态变量以及函数形参),对于基本类型的变量,则直接存储它的值,对于引用类型的变量,则存储的是指向对象的引用,局部变量表的大小在编译器就确定其大小了,因此在程序执行期间,局部变量表大小是不会改变的。
相关问题整理:
1.每个应用程序都拥有一个JVM。
2.java源文件通过编译器编译为字节码文件,再加载到内存中。
3.java内存分为:线程共享内存和线程私有内存
(1)线程私有内存:pc程序寄存器、本地方法栈、java栈
(2)线程共享内存:堆和方法区