美文网首页
JVM--内存模型

JVM--内存模型

作者: aruba | 来源:发表于2021-09-27 20:51 被阅读0次
    安卓高级工程师想要做性能优化、NDK、设计架构时的健全性等工作时,必须是对JVM有一定的了解。技术的路越往上走,就越需要对底层的理解。计算机原理、c/c++语言、JVM原理、数据结构与算法等知识缺一不可。在学习的过程中,一开始觉得知识是线性的,就像一个数组,只需要不断往里面填充数据,然而后面越学习,越发现知识是广度的,到达一定程度后,我们想学习一个知识,往往需要学习一连串的其他知识,其中不乏需要使用其他编程语言,也渐渐明白了全栈工程师的由来,技术的路上没有尽头,只有不断的学习
    JVM的内容偏向概念,不像代码执行就能够看到结果,总结起来也比较麻烦,有条件的还是看下JVM的相关书籍比较好,总结不到位的地方也可以相互探讨

    一.JVM内存模型

    Java是一种解释型语言,首先需要编译成class文件,再交由JVM装载,最终JVM会解释成系统可以直接运行的机器码。性能方面肯定是不如直接编译成机器码的,那么为什么Java不直接编译成机器码呢?因为不同操作系统识别机器码的规则是不同的,而我们写的代码只有一份,JVM来负责转义成不同操作系统下的机器码,带来的好处就是:一次编译,到处运行


    上图为JVM加载class到内存中内存模型,JVM带来的好处除了跨平台外,还有自动内存分配,这也是我们需要了解的重点,c/c++程序员想要使用大量数据时,需要动态申请内存,并在适当的时机手动释放这片内存,一旦忘记释放,造成野指针,那么就会内存泄漏。而JVM帮助我们从申请内存、释放内存的繁琐工作中释放出来

    二、线程共享与私有

    JVM在运行程序时,分成两块数据区,共享数据区和私有数据区

    1.共享数据区

    试想以下我们Java代码中哪些代码是固定的?哪些代码是动态的?
    固定的可以用文本来表示,比如类名、类中的属性、常量、方法名、代码执行顺序等。其实就是我们写的代码
    动态的是文本无法表示的,如变量进行一系列运算得到的值。其实就是需要cpu介入的运算

    接下来,看一段代码:

    public class Hello {
    
        public int test() {
            int a = 3;
            int b = 4;
    
            return a + b;
        }
    
        public static void main(String[] args) {
            Hello h = new Hello();
            h.test();
        }
    
    }
    
    
    1.1方法区

    我们使用javac执行编译后,得到class文件,如果我们运行该程序,执行了main函数,其中对hello对象进行了实例化,那么JVM会加载该class文件,并存储到方法区(元空间)

    Hello.class加载.png
    此时的class文件已经被转义了,我们可以使用javap命令来反编译查看上面代码在JVM是什么样子的
    javap -v Hello.class
    

    不需要细看,后面会详细介绍test方法的内容

    Classfile /C:/Users/tyqhc/Documents/javaworkspace/myJava/out/production/myJava/Hello.class
      Last modified 2021-9-27; size 525 bytes
      MD5 checksum 74b6dc87932a59d4af95208b0eb1edb7
      Compiled from "Hello.java"
    public class Hello
      SourceFile: "Hello.java"
      minor version: 0
      major version: 51
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #5.#25         //  java/lang/Object."<init>":()V
       #2 = Class              #26            //  Hello
       #3 = Methodref          #2.#25         //  Hello."<init>":()V
       #4 = Methodref          #2.#27         //  Hello.test:()I
       #5 = Class              #28            //  java/lang/Object
       #6 = Utf8               <init>
       #7 = Utf8               ()V
       #8 = Utf8               Code
       #9 = Utf8               LineNumberTable
      #10 = Utf8               LocalVariableTable
      #11 = Utf8               this
      #12 = Utf8               LHello;
      #13 = Utf8               test
      #14 = Utf8               ()I
      #15 = Utf8               a
      #16 = Utf8               I
      #17 = Utf8               b
      #18 = Utf8               main
      #19 = Utf8               ([Ljava/lang/String;)V
      #20 = Utf8               args
      #21 = Utf8               [Ljava/lang/String;
      #22 = Utf8               h
      #23 = Utf8               SourceFile
      #24 = Utf8               Hello.java
      #25 = NameAndType        #6:#7          //  "<init>":()V
      #26 = Utf8               Hello
      #27 = NameAndType        #13:#14        //  test:()I
      #28 = Utf8               java/lang/Object
    {
      public Hello();
        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 1: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                   0       5     0  this   LHello;
    
      public int test();
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=3, args_size=1
             0: iconst_3
             1: istore_1
             2: iconst_4
             3: istore_2
             4: iload_1
             5: iload_2
             6: iadd
             7: ireturn
          LineNumberTable:
            line 4: 0
            line 5: 2
            line 7: 4
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                   0       8     0  this   LHello;
                   2       6     1     a   I
                   4       4     2     b   I
    
      public static void main(java.lang.String[]);
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=2, args_size=1
             0: new           #2                  // class Hello
             3: dup
             4: invokespecial #3                  // Method "<init>":()V
             7: astore_1
             8: aload_1
             9: invokevirtual #4                  // Method test:()I
            12: pop
            13: return
          LineNumberTable:
            line 11: 0
            line 12: 8
            line 13: 13
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                   0      14     0  args   [Ljava/lang/String;
                   8       6     1     h   LHello;
    }
    
    

    Java中每一个类(我们不讨论内部类)对应一个class文件,class文件的信息(类元信息)是存放在方法区的,这是共享数据区,并且同一个类加载器只会加载一份,当我们需要实例化该类的对象时,会从方法区查找,如果以前加载过了,那么直接使用。谁都可以实例化这个类,自然它就是存放在共享数据区了

    1.2堆

    方法区存放着class信息,而堆中存放了实例化的对象,同一个类的对象可以被实例化多次,对象是可以被其他线程使用的,所以堆也是共享数据区。实例化对象时,对象中有一个对象头,其中有个类型指针会指向方法区的类元信息,下面的图看看就好,不必深究

    对象头组成.png

    2.私有数据区

    除了方法区和堆,其他的都是私有数据区,前面已经提到了,私有数据区都是方法运行时的数据,需要cpu介入

    2.1栈帧

    Java栈也称为虚拟机栈、线程栈,叫什么其实无所谓,重要的是里面的内容:栈帧

    栈帧主要分为四个部分组成:

    • 局部变量表:存放着局部变量以及其值
    • 操作数栈:存放着运算时的临时数据
    • 动态链接:多态相关
    • 方法出口:进入到下一个方法的栈桢中

    每个方法调用时,都会新建一个栈帧,然后执行方法中的代码,上面test方法中的汇编指令为:

             0: iconst_3
             1: istore_1
             2: iconst_4
             3: istore_2
             4: iload_1
             5: iload_2
             6: iadd
             7: ireturn
    

    用一张gif图,来表示test方法是如何在栈帧中操作的:


    2.2程序计数器与本地方法栈

    程序计数器:程序计数器就是临时记录方法运行到哪一行了,程序运行实际并不存在并行,而是不同的线程不断的抢占cpu,然后执行一段时间,又重新开始竞争,只是执行时间太短,导致人根本感受不到,当一个方法运行到某一行的时候开始重新竞争了,就需要记录下当前方法运行到哪了,用来下次cpu被该方法抢占时,从上次中断的地方继续执行

    本地方法栈:本地方法栈用来运行c/c++代码,结构和Java栈是相同的,区别是c/c++代码动态申请的内存由自己手动管理

    对于内存模型,只要了解下就行了,对于移动端开发,我们做内存优化,针对的是堆中的内存,下一篇将介绍堆中的内存结构

    相关文章

      网友评论

          本文标题:JVM--内存模型

          本文链接:https://www.haomeiwen.com/subject/edhcnltx.html