关于java:JVM系统学习之路运行时数据区概述和程序计数器

15次阅读

共计 3022 个字符,预计需要花费 8 分钟才能阅读完成。

JVM 零碎学习之路系列演示代码地址:
https://github.com/mtcarpenter/JavaTutorial

本篇将 运行时数据区概述及线程  和 程序计数器 的知识点因为不是很多所以就一起梳理,也是为后续学习的知识点做一个铺垫。

运行时数据区概述

运行时数据区,它是在类加载实现后的阶段,如果对类加载不是很相熟的小伙伴,能够看我上一篇文章。

当咱们通过后面的:类的加载 -> 验证 -> 筹备 -> 解析 -> 初始化  这几个阶段实现后,就会用到执行引擎对咱们的类进行应用,同时执行引擎将会应用到咱们运行时数据区,如下

内存是十分重要的系统资源,是硬盘和 CPU 的两头仓库及桥梁,承载着操作系统和应用程序的实时运行 JVM 内存布局规定了 Java 在运行过程中内存申请、调配、治理的策略,保障了 JVM 的高效稳固运行。不同的 JVM 对于内存的划分形式和管理机制存在着局部差别。联合 JVM 虚拟机标准,来探讨一下经典的 JVM 内存布局。

咱们通过磁盘或者网络 IO 失去的数据,都须要先加载到内存中,而后 CPU 从内存中获取数据进行读取,也就是说内存充当了 CPU 和磁盘之间的桥梁

首先还是一张运行时数据区的残缺图:

Java 虚拟机定义了若干种程序运行期间会应用到的运行时数据区,其中有一些会随着虚拟机启动而创立,随着虚拟机退出而销毁。另外一些则是与线程一一对应的,这些与线程对应的数据区域会随着线程开始和完结而创立和销毁。

灰色的为独自线程公有的,红色的为多个线程共享的。即:

  • 每个线程:独立包含程序计数器、栈、本地栈。
  • 线程间共享:堆、堆外内存(永恒代或元空间、代码缓存)

线程

线程是一个程序里的运行单元。JVM 容许一个利用有多个线程并行的执行。在 Hotspot JVM  里,每个线程都与操作系统的本地线程间接映射。

  • 当一个 Java 线程筹备好执行当前,此时一个操作系统的本地线程也同时创立。Java 线程执行终止后,本地线程也会回收。

操作系统负责所有线程的安顿调度到任何一个可用的 CPU 上。一旦本地线程初始化胜利,它就会调用 Java 线程中的 run() 办法。

JVM 零碎线程

如果你应用 console 或者是任何一个调试工具,都能看到在后盾有许多线程在运行。这些后盾线程不包含调用 public static void main(String[]) 的 main 线程以及所有这个 main 线程本人创立的线程。这些次要的后盾零碎线程在 Hotspot JVM  里次要是以下几个:

  • 虚拟机线程:这种线程的操作是须要 JVM 达到平安点才会呈现。这些操作必须在不同的线程中产生的起因是他们都须要 JVM 达到平安点,这样堆才不会变动。这种线程的执行类型包含 “stop-the-world” 的垃圾收集,线程栈收集,线程挂起以及偏差锁撤销。
  • 周期工作线程:这种线程是工夫周期事件的体现(比方中断),他们个别用于周期性操作的调度执行。
  • GC 线程:这种线程对在 JVM 里不同品种的垃圾收集行为提供了反对。
  • 编译线程:这种线程在运行时会将字节码编译成到本地代码。
  • 信号调度线程:这种线程接管信号并发送给 JVM,在它外部通过调用适当的办法进行解决。

程序计数器(PC Register)介绍

JVM 中的 程序计数寄存器 Program Counter Register)中,Register 的命名源于 CPU 的寄存器,寄存器存储指令相干的现场信息。CPU 只有把数据装载到寄存器才可能运行。
这里,并非是狭义上所指的物理寄存器,或者将其翻译为 PC 计数器(或指令计数器)会更加贴切(也称为程序钩子),并且也不容易引起一些不必要的误会。JVM 中的 PC 寄存器是对物理 PC 寄存器的一种形象模仿。

程序计数器是一块很小的内存空间,简直能够疏忽不记。也是运行速度最快的存储区域。
在 JVM 标准中,每个线程都有它本人的程序计数器,是线程公有的,生命周期与线程的生命周期保持一致。
任何工夫一个线程都只有一个办法在执行,也就是所谓的以后办法。程序计数器会存储以后线程正在执行的 Java 办法的 JVM 指令地址;或者,如果是在执行 native 办法,则是未指定值(undefned)。
它是程序控制流的指示器,分支、循环、跳转、异样解决、线程复原等根底性能都须要依赖这个计数器来实现。字节码解释器工作时就是通过扭转这个计数器的值来选取下一条须要执行的字节码指令。
它是惟一一个在 Java 虚拟机标准中没有规定任何 outotMemoryError 状况的区域。

作用

PC 寄存器 用来存储指向下一条指令的地址,也行将要执行的指令代码。由执行引擎读取下一条指令。

举例说明

通过写一个简略的代码演示:

/** 程序计数器
 */
public class PCRegisterTest {public static void main(String[] args) {
        int i = 10;
        int j = 20;
        int k = i + j;
    }
}

将下面的 java 文件编译成字节码文件,而后执行 javap -c PCRegisterTest.class`,通过控制台查看,发现在字节码的右边有一个行号标识,它其实就是指令地址,用于指向以后执行到哪里。

  public static void main(java.lang.String[]);
    Code:
       0: bipush        10
       2: istore_1
       3: bipush        20
       5: istore_2
       6: iload_1
       7: iload_2
       8: iadd
       9: istore_3
      10: return
}

通过 PC 寄存器,咱们就能够晓得以后程序执行到哪一步了

两个常见问题

应用 PC 寄存器存储字节码指令地址有什么用呢?

因为 CPU 须要不停的切换各个线程,这时候切换回来当前,就得晓得接着从哪开始继续执行。
JVM 的字节码解释器就须要通过扭转 PC 寄存器 的值来明确下一条应该执行什么样的字节码指令。

PC 寄存器为什么被设定为公有的?

咱们都晓得所谓的多线程在一个特定的时间段内只会执行其中某一个线程的办法,CPU 会不停地做工作切换,这样必然导致常常中断或复原,如何保障分毫无差呢?为了可能精确地记录各个线程正在执行的以后字节码指令地址,最好的方法天然是为每一个线程都调配一个 PC 寄存器 ,这样一来各个线程之间便能够进行独立计算,从而不会呈现互相烦扰的状况。
因为 CPU 工夫片轮限度,众多线程在并发执行过程中,任何一个确定的时刻,一个处理器或者多核处理器中的一个内核,只会执行某个线程中的一条指令。
这样必然导致常常中断或复原,如何保障分毫无差呢?每个线程在创立后,都会产生本人的程序计数器和栈帧,程序计数器在各个线程之间互不影响。

CPU 工夫片

CPU 工夫片即 CPU 调配给各个程序的工夫,每个线程被调配一个时间段,称作它的工夫片。
在宏观上:咱们能够同时关上多个应用程序,每个程序并行不悖,同时运行。
但在宏观上:因为只有一个 CPU,一次只能处理程序要求的一部分,如何解决偏心,一种办法就是引入工夫片,每个程序轮流执行。

总结

本篇回顾,第一部分为运行时数据区,第二局部就是程序计数器。JVM 能高效稳固运行,次要是 JVM 内存布局规定了 Java 在运行过程中内存申请、调配、治理的策略。接下来会通过很多的章节讲述运行时数据区下的各个区。还须要理解PC 寄存器 用来存储指向下一条指令的地址,也行将要执行的指令代码。由执行引擎读取下一条指令。


欢送关注公众号 山间木匠, 我是小春哥,从事 Java 后端开发,会一点前端、通过继续输入系列技术文章以文会友,如果本文能为您提供帮忙,欢送大家关注、点赞、分享反对,_咱们下期再见!_

正文完
 0