1.JVM内存结构&运行时数据区
运行时数据区定义:Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域。
JVM中主要分为:堆、方法区、程序计数器、虚拟机栈、本地方法栈。
线程私有区域(线程独享):程序计数器、虚拟机栈、本地方法栈;
线程共享区域(线程共享):堆、方法区;
除此之外,还有个直接内存(堆外内存),虽然不是运行时数据区的一部分,但是也会频繁使用,可以理解为操作系统未被虚拟化的一部分内存。
1.1 程序计数器
较小内存空间,当前线程执行代码的行号指示器,各线程间独立存储,互不影响。
主要记录线程执行的字节码的地址,如分支、循环、跳转、异常、线程恢复等都依赖计数器。
Java 是多线程语言,当执行的线程数量超过 CPU 核数时,线程之间会根据时间片轮询争夺 CPU 资源。如果一个线程的时间片用完了,或者是其它原因导致这个线程的 CPU 资源被提前抢夺,那么这个退出的线程就需要单独的一个程序计数器,来记录下一条运行的指令。
JVM是虚拟机,内部有完整的指令与执行的一套流程,运行Java程序时需要计数器记录字节码执行的地址或行号;如果是本地方法(native方法)不需要JVM执行,则不需要计数器。程序计数器也是 JVM 中唯一不会 OOM(OutOfMemory)的内存区域。
1.2 虚拟机栈
方法的执行和栈帧结构见第2节。
虚拟机栈 作用:存储当前线程执行方法的数据,指令,返回地址。
虚拟机栈基于线程:哪怕只有一个main方法,在线程生命周期中,参与计算的数据都会频繁出入栈,栈的生命周期与线程一样。
虚拟机栈的大小缺省为 1M,可用参数 –Xss 调整大小,例如-Xss256k。
栈帧4部分:局部变量表、操作数栈、动态链接、方法返回地址。
局部变量表:用于存储局部变量(一般是方法中变量)。它32位的长度为一个槽slot,Java中8大基础数据类型,如果是32位,直接占用一个slot,如果64位就用高低位占用两个slot。如果是局部一些对象,如Object对象,只需要存它的引用地址。(基本数据类型、对象引用、returnAddress 类型)
编译时已经确定局部变量表、操作数栈大小,可在Class文件中查看;
局部变量表已槽(slot)为单位,一个slot32位,64位的数据可按高低位占两个slot(基本类型和引用类型占1个slot,long和double占2个slot);
方法执行中,虚拟机用局部变量表来实现参数传递过程。如果是实例方法,局部变量表第0位slot存储值默认为方法所属的实例this,其余参数从1开始依次存储;
操作数栈:存放Java操作数,栈结构,用来操作数据,操作的元素可以是Java任意类型,通过依次执行指令来操作。操作数栈本质上是JVM执行引擎的一个工作区,只有方法执行的时候才会进行数据进栈出栈操作,代码不执行时栈为空。
编译期确定大小;
Frame创建时,栈为空,可以存Java各种类型,long和double占2个栈深;
操作栈调用其它有返回结果的方法时,会把结果 push 到栈上(通过操作数栈来进行参数传递);
动态链接:Java语言多态性(后续结合class与执行引擎理解)// TODO
每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态链接;
在类加载阶段中的解析阶段会将符号引用转为直接引用,这种转化也称为静态解析。另外的一部分将在运行时转化为直接引用,