JVM

JVM笔记

Posted by kyle on December 7, 2018
  • 多线程共享的内存区域主要存放两类数据:类数据和类实例(也就是对象)。对象数据存放在堆(Heap)中,类数据存放在方区(Method Area)中。类数据包括字段和方法信息、方法的字节码、运行时常量池,等等。

  • 每个线程都有自己的PC寄存器和JAVA虚拟机栈。JAVA虚拟机栈又由栈帧构成,帧中保存方法执行的状态,包括局部变量表和操作数栈等。在任意时刻,某一线程肯定是在执行某个方法,这个方法叫做该线程的当前方法。执行该方法的帧叫做线程的当前帧。声明该方法的类叫做当前类。如果当前方法是JAVA方法,则PC寄存器中存放正在执行的JAVA虚拟机指令的地址,否则,当前方法是本地方法,PC寄存器中的值没有明确定义。

  • 如果Java虚拟机栈有大小限制,且执行线程所需的栈空间超出了这个限制,会导致StackOverflowError异常抛出。如果Java虚拟机栈可以动态扩展,但是内存已经耗尽,会导致OutOfMemoryError异常抛出。

  • 类或接口的方法信息就放在class文件的method_info结构中。如果方法不是抽象的,也不是本地方法,方法的Java代码就会被编译器编译成字节码(即使方法是空的,编译器也会生成一条return语句),存放在method_info结构的Code属性中。

  • Java虚拟机使用的是变长指令。每条指令都以一个单字节的操作码(opcode)开头,这就是字节码名称的由来。由于只使用一字节表示操作码,显而易见,Java虚拟机最多只能支持256(28)条指令。到第八版为止,Java虚拟机规范已经定义了205条指令,操作码分别是0(0x00)到202(0xCA)、254(0xFE)和255(0xFF)。这205条指令构成了Java虚拟机的指令集(instructionset)。和汇编语言类似,为了便于记忆,Java虚拟机规范给每个操作码都指定了一个助记符(mnemonic)。比如操作码是0x00这条指令,因为它什么也不做,所以它的助记符是nop(nooperation)。

  • 操作数栈和局部变量表只存放数据的值,并不记录数据类型。结果就是:指令必须知道自己在操作什么类型的数据。这一点也直接反映在了操作码的助记符上。例如,iadd指令就是对int值进行加法操作;dstore指令把操作数栈顶的double值弹出,存储到局部变量表中;areturn从方法中返回引用值。也就是说,如果某类指令可以操作不同类型的变量,则助记符的第一个字母表示变量类型。

  • Java虚拟机规范把已经定义的205条指令按用途分成了11类,分别是:常量(constants)指令、加载(loads)指令、存储(stores)指令、操作数栈(stack)指令、数学(math)指令、转换(conversions)指令、比较(comparisons)指令、控制(control)指令、引用(references)指令、扩展(extended)指令和保留(reserved)指令。

  • 保留指令一共有3条。其中一条是留给调试器的,用于实现断点,操作码是202(0xCA),助记符是breakpoint。另外两条留给Java虚拟机实现内部使用,操作码分别是254(0xFE)和266(0xFF),助记符是impdep1和impdep2。这三条指令不允许出现在class文件中。

  • 在运行时常量池中或堆区中,byte占8位、short占16位、int占32位、long占64位。但在栈帧中(即这些数据类型作为局部变量存在的时候),byte、short、int均占32位。

  • 指令分类

  • 方法区主要存放从class文件获取的类信息,此外,类变量(静态变量)也存放在方法区中。当虚拟机第一次使用某个类时,它会搜索类路径,找到相应的class文件,然后读取并解析class文件,把相关信息放进方法区。

  • 运行时常量池主要存放两类信息:字面量(literal)/常量符号引用(symbolic reference)。字面量包括整数、浮点数和字符串字面量;符号引用包括类符号引用、字段符号引用、方法符号引用和接口方法引用。

  • Class结构体中,loader字段存放类加载器指针。

  • 运行时常量池主要存放两类信息:字面量(literal)和符号引用(symbolic reference)。字面量包括整数、浮点数和字符串字面量;符号引用包括类符号引用、字段符号引用、方法符号引用和接口方法符号引用。