JVM基础知识回顾总结

文章目录

  • JVM加载类的过程
  • JVM内存模型
      • 程序计数器
      • 本地方法栈
      • JVM虚拟机栈(线程私有)
          • 栈帧结构
      • 方法区

学完就要做总结,忘啦等于白学!

jvm主要分成3个系统,类加载器系统,运行时数据区,执行引擎

执行引擎是什么东东? 其实就是c++的库函数,运行到最后都是要调用c++里的东西,

JVM加载类的过程

首先由类加载器读取编译后的class字节码文件到直接内存中,之后再根据class content的内容解析成class对象 这里的类加载器有三种,系统类加载器,扩展类加载器,启动类加载器(最顶级的类加载器,用c,c++写的),首先自己写的类,会由系统类加载器加载,但是由于类加载器的双亲委派机制,会委托加载器的上一级加载器负责帮忙加载,如果上一级加载器无法完成加载,则由自身加载器负责加载在加载的过程又分为3步,验证,准备,解析!

  • 验证阶段: 负责字节码文件格式的校验,是否符合jvm的规范是否会危害到jvm
  • 准备阶段 :开始为类的静态成员变量分配内存,并赋予初始值0或null
  • 解析阶段 : 负责将符号引用转为直接引用

符号引用:也就是虚拟地址,因为在jvm运行字节码前,类的成员都还没有分配内存,这时不可能用直接引用,而是用符号代表指向这个成员的引用地址,符号引用存储在常量池中,在加载器的解析阶段,因为已经为一些静态变量分配啦内存,就可以通过解析这个符号得到字段或方法的真正地址,但具体如何实现解析的暂不清楚;

之后是初始化阶段(intializing) 真正的为类的静态成员变量赋初值!
JVM基础知识回顾总结_第1张图片

JVM内存模型

程序计数器

负责存储指令地址(红框圈住的),指令地址存储的有详细指令即关于要进行的操作,程序的运行就是jvm通过寄存器中的指令地址不断更改从而来进行不同的操作,每个线程都有独有的寄存器;
JVM基础知识回顾总结_第2张图片

JVM基础知识回顾总结_第3张图片
JVM基础知识回顾总结_第4张图片

本地方法栈

JVM虚拟机栈(线程私有)

再细分为栈帧,一个方法对应一个栈帧,栈帧又能再分为4个部分,每当执行字节码到一个方法时,都会再开辟一个逻辑意义上的栈帧,说他是逻辑意义,因为jvm并没有栈帧这个真实的物理结构

栈帧结构
  • 局部变量表
    容量在加载阶段就已确定,基础单位slot,也是一段数组,有索引,存储的是方法的形参及方法内定义的局部变量

  • 操作数栈
    类似于局部变量表,就字面意思,存放操作数的,有时在执行一些算法指令时,会把数先压到操作数栈中,运算后再压回局部变量中去

  • 动态链接
    存放指向方法区中常量池里所属方法的地址

  • 返回地址
    cpu运算时靠段寄存器的值加偏移地址得到指令地址进行运算,jvm也是靠寄存器得到下一个指令地址,也就是说在执行某一方法时,把当前寄存器的值(即调用此方法指令的地址)写入这个返回地址,这样方法执行完后便可以返回到调用此方法的地方,然后继续执行下一段指令

方法区

是jvm的一种规范,其作用是存储已经被jvm加载过的类信息,如普通常量,静态变量,方法的一些信息等,如下图

JVM基础知识回顾总结_第5张图片
常量池也在方法区存储,可以看java反编译后的字节码文件中内存结构;如下

//代码部分
public class Jvm {
    static int a1 = 18;

    public static void main(String[] args) {
        final int a = 4;
        int i = 1;
        int j = 2;
        System.out.println(new Jvm().add(1, 2));
    }

    public int add(int i, int j) {
        return i + j;
    }
}
//常量池 存储字面量及符号引用及string指向的子串
//字面量 就是字面的意思,如程序中咱们定义的aij,就是一个utf8的字符串存在常量池里
//符号引用 用特殊字符表示某个字段或方法的地址,比如下方 a1的符号引用i,add方法的符号引用(II)I,等等

Constant pool:
   #1 = Methodref          #9.#33         // java/lang/Object."":()V
   #2 = String             #34            // abcdefg
   #3 = Fieldref           #35.#36        // java/lang/System.out:Ljava/io/PrintStream;
   #4 = Class              #37            // poyimima/algorithm/Jvm
   #5 = Methodref          #4.#33         // poyimima/algorithm/Jvm."":()V
   #6 = Methodref          #4.#38         // poyimima/algorithm/Jvm.add:(II)I
   #7 = Methodref          #39.#40        // java/io/PrintStream.println:(I)V
   #8 = Fieldref           #4.#41         // poyimima/algorithm/Jvm.a1:I
   #9 = Class              #42            // java/lang/Object
  #10 = Utf8               a1
  #11 = Utf8               I
  #12 = Utf8               <init>
  #13 = Utf8               ()V
  #14 = Utf8               Code
  #15 = Utf8               LineNumberTable
  #16 = Utf8               LocalVariableTable
  #17 = Utf8               this
  #18 = Utf8               Lpoyimima/algorithm/Jvm;
  #19 = Utf8               main
  #20 = Utf8               ([Ljava/lang/String;)V
  #21 = Utf8               args
  #22 = Utf8               [Ljava/lang/String;
  #23 = Utf8               s
  #24 = Utf8               Ljava/lang/String;
  #25 = Utf8               a
  #26 = Utf8               i
  #27 = Utf8               j
  #28 = Utf8               add
  #29 = Utf8               (II)I
  #30 = Utf8               <clinit>
  #31 = Utf8               SourceFile
  #32 = Utf8               Jvm.java
  #33 = NameAndType        #12:#13        // "":()V
  #34 = Utf8               abcdefg
  #35 = Class              #43            // java/lang/System
  #36 = NameAndType        #44:#45        // out:Ljava/io/PrintStream;
  #37 = Utf8               poyimima/algorithm/Jvm
  #38 = NameAndType        #28:#29        // add:(II)I
  #39 = Class              #46            // java/io/PrintStream
  #40 = NameAndType        #47:#48        // println:(I)V
  #41 = NameAndType        #10:#11        // a1:I
  #42 = Utf8               java/lang/Object
  #43 = Utf8               java/lang/System
  #44 = Utf8               out
  #45 = Utf8               Ljava/io/PrintStream;
  #46 = Utf8               java/io/PrintStream
  #47 = Utf8               println
  #48 = Utf8               (I)V

按照区域分为三部分,新生代,老年代,及方法区(又叫非堆,特意与堆分开,其本质还是堆的一部分)(1.7以前的实现是永久代,1.8后的实现是元空间),新生代占据1/3的空间,老年代占据2/3的空间,new 出的对象都存放在堆中

  • 新生代(又分为Eden区,(幸存者者1区from区,幸存者2区to区))
    每一个新new 出的对象都会放在Eden区存储,当Eden区满啦以后,gc线程会对Eden区进行一次(轻gc)ygc垃圾回收,对象每经历过一次gc年龄就会+1,经历过gc幸存下来的对象会放到from区,当Eden区再次满之后,会对Eden区及from区进行一次轻gc,仍幸存下来的的对象会放到to区,此时空下来的from区会转变为to区,反正谁空着谁就是to区,就这样每次进行gc后,这两个区域都会进行交换,到某些对象年龄达到15后,就要将其放入老年代啦
  • 老年代
    进入老年代对象的条件: 对象年龄达到15; 同年龄对象所占内存大于from区的一半; gc过后,from区依旧满,存活下的对象放入老年代(属于极端情况)

你可能感兴趣的:(Java,jvm,数据结构,jvm,java)