美文网首页Java 哪些事儿
JVM系列(一):JVM内存模型

JVM系列(一):JVM内存模型

作者: 疏花 | 来源:发表于2019-07-26 17:47 被阅读0次

一、JVM介绍

JVM(Java virtual machine)是一种虚拟机,本身用C语言编写,用来屏蔽不同操作系统的细节,使得Java代码经过一次编译即可在不同的系统上运行。如图所示:
我们用javac命令,就是将Java源文件(.java)编译成Java字节码文件(.class)文件,而jvm会把.class文件翻译成机器码以实现Java的跨平台性。
这里跟C和C++做个比较:

  • C和C++语言也是跨平台的,不过他们的跨平台是在编译器级别的,不同平台要用不同编译器来编译代码,并且在源码级别要对不同平台做相应处理,所以C和C++有“一次编写,到处编译”的说法。
  • Java的跨平台是通过JVM实现的,JVM帮开发者屏蔽了不同平台的细节,同样的源码只经过一次编译就可以在不同的平台上运行,这也就是Java的“一次编写,到处运行”。同时这也是Java速度比C、C++慢的原因,因为Java代码要经过JVM的翻译才可以跟操作系统交互,并且Java的垃圾回收机制也会消耗大量时间。


二、JVM的内存模型

JVM的内存模型即Java的运行时数据区,也就是Java程序在运行时,各种数据存放的地方,基本结构如图:




图一是JVM内存基本结构,分为线程共享区和非线程共享区,图二是比较详细的版本,其中堆又分为新生代和老年代,新生代又分为Eden区和两个Survivor区,下面针对图一来详细说明JVM内存结构。

  1. 非线程共享区
  • 程序计数器: 程序计数器是用于标识当前线程执行的字节码文件的行号指示器。多线程情况下,每个线程都具有各自独立的程序计数器,所以该区域是非线程共享的内存区域。
    通俗点说,程序计数器用来记录当前线程的代码执行到哪里了,那么为啥要记录呢?我一行一行执行不就行了吗?其实在多线程环境中,如果当前线程被挂起了,那恢复的时候怎么知道上次执行到哪里了?这时候程序计数器的作用就来了。
    比如说有个Hello.java:
public class Hello {

    int add() {
        int a = 1;
        int b = 2;
        int c = a + b;
        return c;
    }

    public static void main(String[] args) {
        Hello h = new Hello();
        int result = h.add();
        System.out.println(result);
    }
}

我们用javap -c命令来对class文件进行反汇编:

Compiled from "Hello.java"
public class file.Hello {
  public file.Hello();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  int add();
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_2
       3: istore_2
       4: iload_1
       5: iload_2
       6: iadd
       7: istore_3
       8: iload_3
       9: ireturn

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class file/Hello
       3: dup
       4: invokespecial #3                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #4                  // Method add:()I
      12: istore_2
      13: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      16: iload_2
      17: invokevirtual #6                  // Method java/io/PrintStream.println:(I)V
      20: return
}

代码中Code下的行号,就是程序计数器了。

  • 虚拟机栈
    虚拟机栈描述的是Java方法执行时的内存,每个方法在执行时都会创建一个栈帧(stack frame),每个方法从开始到结束都对应着一个栈帧从入栈到出栈的过程。栈帧又包含了局部变量表、操作数栈、动态链接和方法出口4个部分。

    • 局部变量表:存放方法参数和局部变量
    • 操作数栈: 操作数栈可理解为java虚拟机栈中的一个用于计算的临时数据存储区。以上面Hello类为例,
      iconst_1的作用就是定义第一个int常量,istore_1则将该值压入操作数栈,等运算的时候再进行弹栈(iload_1)。
    • 动态链接: 在运行时常量池中存有大量的符号引用,当栈帧A想调用栈帧B的方法时,就需要以B方法的符号引用作为参数,将符号引用转为直接引用来进行调用,这些符号引用一部分在类的加载阶段(解析)或第一次使用的时候就转化为了直接引用(指向数据所存地址的指针或句柄等),这种转化称为静态链接。而相反的,另一部分在运行期间转化为直接引用,就称为动态链接。
    • 方法出口:分为正常完成出口和异常完成出口
      正常完成出口:执行任意一个方法返回(如:return)的字节码指令,如有返回值则返回给调用者。
      异常完成出口:在方法执行过程中发生异常,且没有在方法体中进行处理。异常完成出口退出时,不会给上层调用者任何返回值。
  • 本地方法栈
    和虚拟机栈类似,只不过本地方法栈是为native方法服务的。

  1. 线程共享区
  • 方法区
    存放类信息,如类的字段、方法、常量等信息,在jdk1.8之前,方法区是位于永久代的,在jdk1.8之后,永久代已经被移除,方法区放在了元数据空间里。
    元数据空间(meta area)和之前的永久代类似,存放了类的一些基本数据和常量池,但元空间不是在JVM里的,而是使用的本地内存。


  • 存放Java对象和数组对象的地方,jvm内存结构中,堆占的空间是最多的,也是Java垃圾收集器工作的地方。
    堆分为新生代和老年代,新生代又分为Eden区和两个Survivor区,关于堆和垃圾收集器的内容,放在后面的文章讲解。

相关文章

  • JVM内存模型(jvm 入门篇)

    概述 jvm 入门篇,想要学习jvm,必须先得了解JVM内存模型,JVM内存模型,JVM内存模型,JVM内存模型,...

  • 复习系列-2 jvm内存模型

    复习系列-2 jvm内存模型

  • JVM

    栈容量由-Xss指定深入理解JVM—JVM内存模型 JVM内存模型和JVM参数的关系

  • JVM

    简介 Jvm 系列一:Java类的加载机制Jvm系列二:JVM内存结构 --内存泄漏与内存溢出Jvm系列三:GC算...

  • JVM系列:(八)JVM内存模型

    原文链接:JVM系列:(八)JVM内存模型 一 运行时数据区域 类加载器加载的class字节码保存到JVM内存的方...

  • JVM系列篇:深入详解JVM内存模型与JVM参数详细配置

    本系列会持续更新。 JVM基本是BAT面试必考的内容,今天我们先从JVM内存模型开启详解整个JVM系列,希望看完整...

  • 一文带你深入了解JVM内存模型与JVM参数详细配置

    JVM基本是BAT面试必考的内容,今天我们先从JVM内存模型开启详解整个JVM系列,希望看完整个系列后,可以轻松通...

  • JVM问题及解答

    常见JVM问题 JVM内存模型,GC机制和原理。注意JVM内存模型与Java内存模型(JMM)不是同一个东西。JV...

  • 高效并发

    从JVM的角度看一下Java与线程,内存模型,线程安全以及JVM对于锁的优化 硬件内存模型与JVM内存模型 硬件的...

  • JVM内存结构和Java内存模型

    最近看到两个比较容易混淆的概念:JVM内存结构和Java内存模型 JVM内存结构JVM内存结构或者说内存模型指的是...

网友评论

    本文标题:JVM系列(一):JVM内存模型

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