头图

1 基本概念

1.1 JVM概念

JVM(Java Virtual Machine)Java虚拟机,即一个用于执行Java代码的虚拟机器,JVM可直接运行在不同的操作系统上。Java代码通过编译器编译生成.class字节码文件,字节码文件又经过JVM中的解释器编译成可直接运行的机器码,这也就是Java能够实现跨平台的原因。

1.2 JVM所在位置

JVM是直接运行在操作系统之上, 并不会直接与系统硬件进行交互。

image.png

1.3 运行过程

Java源文件通过编译器编译成字节码文件,字节码文件通过JVM编译成对应平台能够直接执行的机器码,当一个程序开始运行的时候,就会启动一个对应的Java虚拟机实例用来支撑程序的运行,当有多个程序运行时,就会启动多个Java虚拟机实例,Java虚拟机实例随着程序的退出或关闭而消亡,同时多个Java虚拟机实例之间数据不共享。

1.4 运行时数据区

image.png

在运行时数据区中的内存区域主要分为线程共享区域和线程私有区域,这里的线程指的是Java程序中的线程,Java程序中允许一个程序有多个线程,线程共享区域即在整个JVM运行期间只会初始化创建一份,随着JVM的启动而创建,多个线程共用这一份区域中的数据,垃圾回收也发生在当前区域,线程私有区域则是每个线程启动的时候都会随着线程创建一份,线程结束时跟着销毁,每个线程只能访问自己私有区域的数据。

1.4.1 程序计数器

在Java中,当一个Java源代码文件被编译成字节码之后,就形成了一行行的字节码指令,而程序计数器(Program Counter Register)中主要保存的就是当前线程执行的字节码的行号,字节码解释器通过改变程序计数器中保存的行号来获取下一条需要执行的字节码指令。

在任何一个确定的时间,一个处理器(对于多核处理器来说是一个内核)都只会执行一个线程中的一条指令,因此程序计数器最主要的作用是在多线程运行环境下保证线程切换后能够恢复到正确的执行位置,例如当前程序中有A和B两个线程,当A线程执行到第3行指令时,程序计数器中保存的是行号3,这时候CPU被分配到执行线程B的指令,当执行完线程B的指令之后又切换到线程A,这时候通过程序计数器中保存的行号就能立马恢复到需要继续执行的位置。

1.4.2 Java虚拟机栈

Java虚拟机栈(Java Virtual Machine Stacks)是程序运行最主要的一部分,对应的Java方法执行的内存模型,当一个Java方法执行时,都会对应的创建一个栈帧(Stack Frame),栈帧中主要保存的信息有局部变量表(八大基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用、returnAddress类型)、操作数栈、动态链接和方法出口等信息,一个Java方法从被调用到执行结束对应的就是一个栈帧的入栈出栈的过程,程序当前执行的方法一定在栈顶。

image.png

例如在一个应用程序中有方法A和方法B,在方法A中调用了方法B,那么在执行方法A时就会生成一个栈帧,并将方法A对应的栈帧压入到虚拟机栈中,当方法A中调用方法B的时候又会生成一个对应方法B的栈帧,并将其压入到虚拟机栈,此时方法B对应的栈帧在虚拟机栈的顶层,当方法B执行完之后该栈帧就会被弹出,然后方法A对应的栈帧就变成顶层的栈帧,程序就继续执行方法A,当方法A执行完成之后,方法A对应的栈帧被弹出,当前程序也就执行完成。

1.4.3 本地方法栈

本地方法栈(Native Method Stack)与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。

1.4.4 Java堆

Java堆(Java Heap)是Java虚拟机中内存最大的一块,该区域最主要的作用是用来存放对象实例,当一个对象被实例化出来之后就保存在堆空间中,几乎所有的对象实例都在堆空间进行分配内存。

堆空间也是垃圾回收(Garbage Collection)的主要区域,同时由于现在的收集器基本都采用分代回收算法,所以Java堆还被细分为新生代、老年代和永久代(在JDK8以后永久代改为元空间),在新生代中又进一步分为Eden区、From Survivor区和To Survivor区。

image.png

1.4.5 方法区

方法区(Method Area)主要用来存储Java虚拟机加载的类信息、运行时常量池、静态变量和编译后的代码等数据,同时方法区还有一个别名:非堆(Non-Heap)。方法区是JVM规范中的逻辑部分,也就是说方法区并不是具体的实现,而是一个规范,永久代则正是针对方法区的具体实现,但是只存在于jdk7之前的版本中,在jdk8及以后的版本中移除了永久代,取而代之的是元空间(Metaspace),同时,并不是所有的JVM中都有永久代,IBM的j9、Oracle的JRocket中都没有永久代。

方法区是规范,规定了该区域可以存放哪些数据,永久代和元空间是针对方法区不同的具体实现。

参考书籍

  • 《深入理解Java虚拟机》——周志明
本文参与了SegmentFault 思否写作挑战赛活动,欢迎正在阅读的你也加入。

CodeJR
12 声望0 粉丝