Java虚拟机的体系结构

说明:

本文参照的《深入Java虚拟机》第5章内容

本文所说的java虚拟机指的是一个运行中的java虚拟机实例。


一个java虚拟机实例的任务就是:负责运行一个java程序,有着相同的生命周期。

每个java程序都有属于自己的java虚拟机实例,如果在同一台机器上运行3个java程序,将得到3个java虚拟机实例。

一个虚拟机的行为分别是按照子系统,内存区,数据类型以及指令这几个术语来描述的。如下图:


1.类装载器子系统

负责查找并装载类类型,即把描述类的数据从class文件加载到内存,并对数据进行校验,转换分析和初始化,最终形成可以被虚拟机直接使用的JAVA类型。

分为:启动类装载器和用户自定义类装载器。


类的加载顺序为:

1)装载

查找并装载类型的二进制数据

2)连接(验证,准备,解析)

验证:确保被导入类型的正确性

准备:为类(静态)变量分配内存,并初始化为默认值

解析:把存在常量池的类型中的符号引用转换为直接引用,举例如下图:



3)初始化:

将执行类构造器< init>()方法,注意这里的方法不是构造方法。该方法将会显式调用父类构造器,接下来按照java语句顺序为类变量和静态语句块赋值

把类(静态)变量初始化为正确的初始值。

类被初始化的5种情况:1.new一个对象 ;2.初始化其子类;3.调用main方法;4.调用静态变量(final修饰的编译时常量除外);5.调用反射方法。


从类的角度看,类在虚拟机内存的生命周期包括:加载->连接(验证->准备->解析)->初始化->使用->卸载。

当虚拟机加载一个class类文件时,他会从class文件包含的二进制数据中解析类型信息,并把这些类型信息(数据结构)放入方法区中。当程序运行时,虚拟机会把运行时创建的该对象示例都放到堆中。


2.方法区

用来存储基本类型信息(类权限定名,直接超类权限定名,类类型,访问修饰符等),常量池,字段 方法信息,静态变量,一个到classLoader的引用,一个到Class类的引用等的区域,线程共享。

常量池:包括直接常量(String,integer和float),和对其他类型,字段和方法的符号引用。

方法信息:方法名,返回类型,参数数量及类型,修饰符,方法的字节码,异常表,操作数栈和该方法栈帧中的局部变量大小

3.堆

用来存储java程序运行时创建的所有实例或数组,线程共享。堆里的对象由垃圾收集器(GC)自动管理。


4.pc寄存器

存的是下一条即将被执行指令的“地址”。如果该线程正在执行一个本地方法,那么pc里的当前值是“undefined”

5.java栈

每当启动一个新线程,java虚拟机会为其分配一个java栈,线程私有。java栈以帧为单位,保存线程的运行状态,每一个帧代表线程调用的一个java方法。

对java栈的操作只有两只:以桢为单位的压栈和出栈。


栈帧:有3部分---局部变量区,操作数栈,帧数据区。

帧数据区:用于常量池解析,正常方法返回,异常中止处理。

一个方法运行的栈帧,如下图:



一个方法调用另一个方法的栈运行图,如下:



6.本地方法栈

为本地方法提供服务的。一个线程在执行java方法的过程中,调用了本地方法的例子如下图:

7.执行引擎

负责执行被装载类的方法中的指令,有一套对应的指令集。

字节码执行是执行引擎是最重要的一部分,概念模型的总体外观是一致的:输入字节码,过程是字节码解析的等效过程,输出结果。不同的虚拟机有不同的具体实现,大体有解释执行和编译执行两种选择。方法的字节码流是由指令序列构成,每一条指令包含一个操作码,0个或多个操作数。

java指令集是以栈为中心设计的,可以更好得实现平台无关性。


另添加以下相关内容:

类文件结构

class类文件是一组以8位字节为基础的二进制流,它包含以下几个部分:

魔数:类文件开头的四个字节被定义为CAFEBABE,只有开头为CAFEBABE的文件才可以被虚拟机接受,接

class文件版本:下来四个字节为class文件的版本号,高版本JDK可以兼容以前版本的class文件,但不能运行以后版本的class文件。

常量池:分为 字面量和符号引用。字面量包含:文本字符串,声明为final的常量值等,符号引用包含:类和接口的全限定名,字段的名称和描述符,方法的名称和描述符。


访问标志:常量池结束后,紧接着两个字节表示访问标志,用于识别一些类或接口层次的访问信息,例如是否是public,是否是static等。

类索引,父类索引,和接口索引集合:类索引用来确定这个类的全限定名,父类为父类的全限定名,接口索引集合为接口的全限定名。

字段表集合:用于描述接口或者类中声明的变量,但不包含方法中的变量。

方法表集合:用于表述接口或者类中的方法。

属性表集合:class文件,字段表,方法表中的属性都源自这里。

例子:查看java源代码对应的class文件

源码如下:

package com.founder.rcp.controller;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Random;

import com.founder.rcp.service.impl.ReserveTrigerServiceImpl;




public class Test {
	public TestCommon c1;
	public ReserveTrigerServiceImpl rt;
	public String s = "1";
	public TestCommon c2 = TestCommon.test;
	
    public static void main(String[] args) {
           System.out.println("主方法");
    }
    
    public String  method1(){
    	System.out.println("方法1");
    	return "1";
    };
}


查看class字节码文件的内容,可以用jdk自带的javap反汇编指令。如下图:

C:\Windows\system32>javap -v -p -c -s D:\workspace_ylt\ylt-yyzl\build\classes\co
m\founder\rcp\controller\Test.class
Classfile /D:/workspace_ylt/ylt-yyzl/build/classes/com/founder/rcp/controller/Te
st.class
  Last modified 2017-11-15; size 968 bytes
  MD5 checksum b90a7113987633afad3d236ca012f973
  Compiled from "Test.java"
public class com.founder.rcp.controller.Test
  SourceFile: "Test.java"
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER//访问标志access_flag
Constant pool://常量池
   #1 = Class              #2             //  com/founder/rcp/controller/Test --C_class_info
   #2 = Utf8               com/founder/rcp/controller/Test
   #3 = Class              #4             //  java/lang/Object
   #4 = Utf8               java/lang/Object
   
//字面量和符号引用
   #5 = Utf8               c1
   #6 = Utf8               Lcom/founder/rcp/controller/TestCommon;
   #7 = Utf8               rt
   #8 = Utf8               Lcom/founder/rcp/service/impl/ReserveTrigerServiceImpl;
   #9 = Utf8               s
  #10 = Utf8               Ljava/lang/String;
  #11 = Utf8               c2 
 
//CONSTANT_Methodref_info表
  #12 = Utf8               <init>
  #13 = Utf8               ()V
  #14 = Utf8               Code
  #15 = Methodref          #3.#16         //  java/lang/Object."<init>":()V
  #16 = NameAndType        #12:#13        //  "<init>":()V
  #17 = String             #18            //  1
  #18 = Utf8               1
  
//CONSTANT_Fieldref_info表( s, c2)
  #19 = Fieldref           #1.#20         //  com/founder/rcp/controller/Test.s:Ljava/lang/String;
  #20 = NameAndType        #9:#10         //  s:Ljava/lang/String;
  #21 = Fieldref           #22.#24        //  com/founder/rcp/controller/TestCommon.test:Lcom/founder/rcp/controller/TestCommon;
  #22 = Class              #23            //  com/founder/rcp/controller/TestCommon
  #23 = Utf8               com/founder/rcp/controller/TestCommon
  #24 = NameAndType        #25:#6         //  test:Lcom/founder/rcp/controller/TestCommon;
  #25 = Utf8               test
  #26 = Fieldref           #1.#27         //  com/founder/rcp/controller/Test.c2:Lcom/founder/rcp/controller/TestCommon;
  #27 = NameAndType        #11:#6         //  c2:Lcom/founder/rcp/controller/TestCommon;
  
  #28 = Utf8               LineNumberTable
  #29 = Utf8               LocalVariableTable
  #30 = Utf8               this
  #31 = Utf8               Lcom/founder/rcp/controller/Test;
  #32 = Utf8               main
  #33 = Utf8               ([Ljava/lang/String;)V
  #34 = Fieldref           #35.#37        //  java/lang/System.out:Ljava/io/PrintStream;
  #35 = Class              #36            //  java/lang/System
  #36 = Utf8               java/lang/System
  #37 = NameAndType        #38:#39        //  out:Ljava/io/PrintStream;
  #38 = Utf8               out
  #39 = Utf8               Ljava/io/PrintStream;
  #40 = String             #41            //  主方法
  #41 = Utf8               主方法
  #42 = Methodref          #43.#45        //  java/io/PrintStream.println:(Ljava/lang/String;)V
  #43 = Class              #44            //  java/io/PrintStream
  #44 = Utf8               java/io/PrintStream
  #45 = NameAndType        #46:#47        //  println:(Ljava/lang/String;)V
  #46 = Utf8               println
  #47 = Utf8               (Ljava/lang/String;)V
  #48 = Utf8               args
  #49 = Utf8               [Ljava/lang/String;
  #50 = Utf8               method1
  #51 = Utf8               ()Ljava/lang/String;
  #52 = String             #53            //  方法1
  #53 = Utf8               方法1
  #54 = Utf8               SourceFile
  #55 = Utf8               Test.java
{
//fields:field_info字段表集合
 public com.founder.rcp.controller.TestCommon c1;
    Signature: Lcom/founder/rcp/controller/TestCommon;
    flags: ACC_PUBLIC


  public com.founder.rcp.service.impl.ReserveTrigerServiceImpl rt;
    Signature: Lcom/founder/rcp/service/impl/ReserveTrigerServiceImpl;
    flags: ACC_PUBLIC


  public java.lang.String s;
    Signature: Ljava/lang/String;
    flags: ACC_PUBLIC


  public com.founder.rcp.controller.TestCommon c2;
    Signature: Lcom/founder/rcp/controller/TestCommon;
    flags: ACC_PUBLIC

//methods:method_info方法表集合
  public com.founder.rcp.controller.Test();
    Signature: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #15                 // Method java/lang/Object."<init>
":()V
         4: aload_0
         5: ldc           #17                 // String 1
         7: putfield      #19                 // Field s:Ljava/lang/String;
        10: aload_0
        11: getstatic     #21                 // Field com/founder/rcp/controlle
r/TestCommon.test:Lcom/founder/rcp/controller/TestCommon;
        14: putfield      #26                 // Field c2:Lcom/founder/rcp/contr
oller/TestCommon;
        17: return
      LineNumberTable:
        line 12: 0
        line 15: 4
        line 16: 10
        line 12: 17
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      18     0  this   Lcom/founder/rcp/controller/Test;

  public static void main(java.lang.String[]);
    Signature: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #34                 // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #40                 // String 主方法
         5: invokevirtual #42                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 19: 0
        line 20: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       9     0  args   [Ljava/lang/String;

  public java.lang.String method1();
    Signature: ()Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #34                 // Field java/lang/System.out:Ljav
a/io/PrintStream;
         3: ldc           #52                 // String 方法1
         5: invokevirtual #42                 // Method java/io/PrintStream.prin
tln:(Ljava/lang/String;)V
         8: ldc           #17                 // String 1
        10: areturn
      LineNumberTable:
        line 23: 0
        line 24: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      11     0  this   Lcom/founder/rcp/controller/Test;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值