深入理解java虚拟机一 JAVA运行时内存区域与class文件

it2025-10-22  5

一 JAVA运行时内存区域

JVM在加载class文件时,会将class文件定义的数据结构转为运行时内存中的数据,那么jvm是如何安排运行时的内存区域呢?

jvm将运行时内存划分为以下几个部分:

堆:所有线程共享

方法区:类信息、静态变量、常量等

  运行时常量池:class文件的常量池(字面常量和符号引用)+运行时产生的常量

程序计数器:  当前线程执行的字节码的行号指示器

虚拟机栈:栈帧 = 本地局部变量表、操作数栈、动态链接、出口信息

本地方法栈:native方法

直接内存:不属于jvm管理,但是在nio中,会使用native方法申请堆外内存,并在java堆中保存其引用。

其中,堆和方法区是所有的线程所共享的,而虚拟机栈、本地方法栈和程序计数器是各线程所独享的。

二 class文件

class文件是怎样定义的呢?它与java运行时内存是什么关系呢?

class文件格式:

u4 魔数

u2 class文件版本号

u2 class文件版本号

u2 constant_pool_count    表示常量池容量大小,从1开始计数;

   常量池中主要存放两大类:字面常量和符号引用

     符号引用又包含三类:类、接口的全限定名,字段名称和描述,方法名称和描述

cp_info constant_pool

   共定义了11种常量项 

    其中,有CONSTANT_Fieldref_info,会有声明字段的类/接口和字段的名称及描述符等;

 

    与后面的字段表/方法表略有不同,但是会复用相同的简单名称和描述符。

    这里的只会包含在类中被使用的field和method。

      当前代码所属的类是D,要把一个符号引用N解析成一个来自类或接口C的直接引用:

  这个发生在何地?可能发生在方法的字节码中:

Classfile /Users/wangzx/IdeaProjects/devep_java/spring_java/target/classes/com/sankuai/sc/t/test2.class Last modified 2016-6-5; size 546 bytes MD5 checksum c1c0289a9a434d71f7e4a04adfd366c3 Compiled from "test2.java"public class com.sankuai.sc.t.test2 SourceFile: "test2.java" minor version: 0 major version: 49 flags: ACC_PUBLIC, ACC_SUPERConstant pool: #1 = Methodref #5.#22 // java/lang/Object."<init>":()V #2 = Fieldref #23.#24 // com/sankuai/sc/t/A.a:I #3 = Methodref #4.#25 // com/sankuai/sc/t/test2.inc:(Lcom/sankuai/sc/t/A;)I #4 = Class #26 // com/sankuai/sc/t/test2 #5 = Class #27 // java/lang/Object #6 = Utf8 m #7 = Utf8 I #8 = Utf8 <init> #9 = Utf8 ()V #10 = Utf8 Code #11 = Utf8 LineNumberTable #12 = Utf8 LocalVariableTable #13 = Utf8 this #14 = Utf8 Lcom/sankuai/sc/t/test2; #15 = Utf8 inc #16 = Utf8 (Lcom/sankuai/sc/t/A;)I #17 = Utf8 a #18 = Utf8 Lcom/sankuai/sc/t/A; #19 = Utf8 inccc #20 = Utf8 SourceFile #21 = Utf8 test2.java #22 = NameAndType #8:#9 // "<init>":()V #23 = Class #28 // com/sankuai/sc/t/A #24 = NameAndType #17:#7 // a:I #25 = NameAndType #15:#16 // inc:(Lcom/sankuai/sc/t/A;)I #26 = Utf8 com/sankuai/sc/t/test2 #27 = Utf8 java/lang/Object #28 = Utf8 com/sankuai/sc/t/A{ public int m; flags: ACC_PUBLIC

 

public com.sankuai.sc.t.test2(); flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 19: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/sankuai/sc/t/test2;

 

int inc(com.sankuai.sc.t.A); flags: Code: stack=2, locals=2, args_size=2 0: aload_1 1: getfield #2 // Field com/sankuai/sc/t/A.a:I 4: iconst_1 5: iadd 6: ireturn LineNumberTable: line 24: 0 LocalVariableTable: Start Length Slot Name Signature 0 7 0 this Lcom/sankuai/sc/t/test2; 0 7 1 a Lcom/sankuai/sc/t/A;

 

int inccc(com.sankuai.sc.t.A); flags: Code: stack=2, locals=2, args_size=2 0: aload_0 1: aload_1 2: invokevirtual #3 // Method inc:(Lcom/sankuai/sc/t/A;)I 5: ireturn LineNumberTable: line 27: 0 LocalVariableTable: Start Length Slot Name Signature 0 6 0 this Lcom/sankuai/sc/t/test2; 0 6 1 a Lcom/sankuai/sc/t/A;}

 

 

      1.如果C本身就包含了简单类型和字段描述符与N都相符的字段,则返回此字段的直接引用。  

      2. 查找D是否有N,需要查找D的字段表    

  3.否则,如果C实现了接口,则在接口中查找

  4.否则,在C的父类中查找。

 

 

u2 access_flag

  是否是类/接口,是否是抽象类,是否是private等,是否是final等

u2 this_class

u2 super_class

u2 interface_count

u2 interfaces

都是指 常量池中的全限定名称,先指向类/接口索引,再找到utf-8的索引

u2 fields_count

field_info fields

字段表中会包含 字段access属性、字段名称索引、描述符索引以及attribute;

描述符:数组 [, V void, L 引用类型, 描述方法时,先用小括号描述参数列表,后面再跟上返回值描述。

字段表中不会列出父类和超类中继承而来的字段名

u2 methods_count

method_info methods

方法表:access属性,名称索引、描述索引以及attribute

如果方法没有被override,则不会出现父类继承而来的方法名

u2 attributes_count

attribute_info attributes

属性表中最重要的要数方法表的Code属性,其中包含了方法的具体信息,具体如下:

u2 attribute_name_index

u4 attribute_length

u2 max_stack 操作数栈的最大深度

u2 max_locals

u4 code_length

u1 code * code_length

....异常表等

 

转载于:https://www.cnblogs.com/cycc/p/5561321.html

最新回复(0)