深入理解Java虚拟机(第三版)01-Java虚拟机运行时数据区介绍

微信公众号:[老张聊天]
又稳又重的老码农,分享工作生活经验。
风趣幽默的段子手,总结编程专业知识。
希望能用通俗易懂的语言,给小伙伴带来收获~
[如果觉得对您有帮助,欢迎关注,转发,点赞!]

一、开篇说明

老张最近在看多线程、锁的源码,但是在学习源码过程中,发现需要对Java虚拟机,以及JMM等有一个全面的理解,能更快的吸收源码中的知识。所以先更新Java虚拟机专栏内容。

之前没有接触过的小伙伴也不用怕不用怂,其实这部分知识,大部分还是理论。推荐先看一遍视频学习,对整体有印象和概念后,再来看书补充细节,多自己画图记忆。最后像老张一样,把学到的知识点巩固一下,整理成博客,最后按照自己的思路、自己的话能让别人听懂,就证明对这部分知识理解透彻了。

视频可以在B站搜索查看,有很多免费教程。

大家都知道Java程序可以转为字节码在Java虚拟机上运行,但是Java虚拟机有HotSpot,OpenJDK等等,一套字节码可以在多套虚拟机上运行,就是因为虚拟机受到《Java虚拟机规范》约束。《Java虚拟机规范》相当于API,而不同的Java虚拟机实现相当于Impl。

除了Java语言,Scala语言等也可以在Java虚拟机上运行,不同的语言都可以编译成字节码。Java受到《Java语言规范》的约束,规定如何声明类,创建方法等等。同理Scala语言也有《Scala语言规范》。

Java编译器同时受到《Java虚拟机规范》与《Java语言规范》的约束。

以下大部分内容基于周志明的《深入理解Java虚拟机 第三版》,第三版相比前面更新了很多知识点。pdf版本已经上传到网盘,微信扫码关注获取网盘地址和提取码

欢迎扫码关注

二、运行时数据区

根据《Java虚拟机规范》的规定,Java虚拟机所管理的内存将会包括以下几个运行时数据区域,如下图所示:
在这里插入图片描述

2.1 先记忆此图【运行时数据区】3分钟

直到可以自己将此图画出为止,在线画图可以借助ProcessOn。

背诵:
运行时数据区分为所有线程共享区域、线程隔离(也叫线程独占)的区域两部分。
共享区域分为方法区和堆(1.8中称为元数据,后面详细解释)。
线程隔离(也叫独占)区域,包括虚拟机栈,本地方法栈,程序计数器。(2栈1计数
运行时数据区,关联执行引擎,本地库接口,本地库接口关联本地方法库。

2.2 运行时数据区概念理解

Java虚拟机在实行Java程序的过程中,会把它所管理的内存划分为若干个不同的数据区域。有些区域是随着虚拟机进程启动而存在,有些是依赖用户线程来建立和销毁。

这里大家可以思考下,常说的垃圾回收主要是在哪片内存区域呢?

2.3 程序计数器

英文要知道Program Counter Register 。

由于Java虚拟机的多线程,是通过线程轮流切换并分配处理器执行时间的方式来实现的。一核的处理器只会执行一条线程中的指令。

那如果这次处理器分配时间太短,线程指令还未结束完,那等处理器第二次执行时,就需要知道第一次执行到哪里才行。

程序计数器,是一块比较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器,保证线程切换后可以继续执行。
**在Java虚拟机的概念模型里,字节码解释器工作时,就是通过改变计数器的值来选取下一条需要执行的字节码指令。
每一个线程都会有自己的程序计数器,大家互不干扰。所以程序计数器,是在线程独占(线程隔离)区域

  • 如果线程执行的是Java方法,则程序计数器记录的是正在执行的虚拟机字节码指令的地址;
  • 如果线程执行的是Native方法,则程序计数器值为Undefined;
  • 程序计数器所在内存区域,是个在Java虚拟机规范中没有规定任何OutOfMemoryError的区域。

2.4 Java虚拟机栈

Java Virtual Machine Stacks

  • 线程私有的,生命周期与线程相同。
  • 描述了Java方法执行的内存模型:每个方法被执行的时候,会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口等信息。
  • 每个方法被调用直至执行完成,就对应一个栈帧在虚拟机栈从入栈到出栈的过程。
  • 之前书本上的堆栈,“栈”就是对应的Java虚拟机栈中的局部变量表部分。
  • 局部变量表存放了编译期可知的各种Java虚拟机基本数据类型,对象引用,和returnAddress类型(指向了一条字节码指令的地址)。这些数据类型在局部变量表中存储空间以局部变量槽(Slot)来表示,其中64位长度的long和double类型的数据占用2个局部变量槽,其余数据类型占用1个。
  • 局部变量表所需的内存空间,编译期间完成分配,方法运行时不会改变局部变量表的大小。这里的大小指的是槽的数量,至于每个槽占用多少个bit,是虚拟机来决定的。
  • Java虚拟规范中,对这个区域规定了两种异常情况:
    a、如果线程请求的栈深入大于虚拟机所允许的深度,则会抛出StackOverflowError异常。
    b、如果虚拟机栈可以动态扩展,当扩展到无法申请足够的内存时,会抛出OutOfMemoryError异常。

注意:Java虚拟机栈也会出现OutOfMemoryError异常

2.5 本地方法栈

Native Method Stacks

  • 与虚拟机栈作用相似,虚拟机栈为执行Java方法服务,本地方法栈为虚拟机使用的Native方法服务。
  • 本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常。

2.6 Java堆

Java Heap

  • 是Java虚拟机所管理的内存中最大的一块,被所有线程共享的一块内存区域。几乎所有的对象实例都在这里分配内存。
  • 虚拟机启动时创建,唯一目的就是存放对象实例。
  • Java堆是垃圾回收管理的主要区域。
  • 不少资料中写着Java虚拟机的堆内存分为新生代,老年代,永久代,Eden,Survivor,十年前(以G1收集器的出现为分界),HotSpot虚拟机内部的垃圾收集器全部基于“经典分代”来设计,需要新生代、老年代收集器搭配才能工作。这种说法还时不会产生太多歧义,但是到了今天,HostSpot里也出现了不采用分代设计的垃圾收集器,再按照这个说法就很多需要商榷的地方。
  • Java堆可以是物理上不连续的内存空间,只要逻辑连续就可以。
  • -Xmx最大值 -Xms最小值

2.7 方法区

Method Area

  • 与Java堆一样,是各个线程的共享区域。
  • 用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
  • 还有一个别名叫Non-Heap(非堆),目的是与Java堆区分开来。
  • 永久代(Permanent Generation) 并不等价于方法区,永久代是方法区实现的一种方式。仅仅是因为HotSpot虚拟机的设计团队,把GC分代收集扩展到了方法区,对于其他虚拟机是不存在永久代的概念的。
  • 永久代实现方法区容易导致内存溢出问题-XX:MaxPermSize上限,即使不设置也有默认值。JDK8中,完全废弃了永久代的概念,改为在本地内存中实现的元空间(Meta-space)来替代。
  • Java虚拟机规范,对这个区域限制非常宽松。不仅不需要连续的内存,还可以选择部不实现垃圾回收。
  • 垃圾回收在这个区域很少存在,但是并不意味真的永久存在了。这个区域主要针对常量池的回收和对类型的卸载。
  • 当方法区无法满足内存分配需求,将抛出OutOfMemoryError异常。

2.8 运行时常量池

Runtime Constant Pool

  • 运行时常量池是方法区的一部分。用于存放编译期生成的各种字面量和符号引用。这部分内容将在类加载后存放到方法区的常量池中。
  • 字面量和符号引用等后面会详细介绍。
  • 运行时常量池,对于Class文件常量池,最重要特征是具备动态性,运行期间也可能将新的常量放入池中。例如String类的intern()方法,具体可以参考博客:https://blog.csdn.net/tyyking/article/details/82496901
  • 常量池无法再申请到内存时也会抛出OutOfMemoryError异常。

2.9 直接内存

Direct Memory

  • 并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。
  • 频繁使用,也可能导致OutOfMemoryError异常。
  • JDK1.4新增了NIO类,引入基于通道与缓冲区的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。
  • 在一些场景显著提高性能,避免了再Java堆和Native堆中来回复制数据。
  • 要避免配置虚拟机参数时,会根据实际内存设置-Xmx等参数,忽略了直接内存,使得各个区域的内存综合大于物理内存限制和操作系统限制,从而导致动态扩展出现OOM。

三、阶段总结

本篇博客名词,理论部分比较多。
初学需要大家强化记忆,后面等学完所有的虚拟机相关知识后,这些概念才会深入理解。

现在我们回忆一下,Java虚拟机运行时数据区有哪些组成部分?并分别用一句话重点概括:

  • Java虚拟机运行时数据区,分为线程共享区域,线程独占区域。
  • 线程共享区域有方法区,和堆。(1.8称为元数据)
  • 线程独占区域有Java虚拟机栈,本地方法栈,程序计数器。
  • 执行引擎关联本地库接口,本地库接口关联本地方法库。

博客内容大部分来自《深入学习Java虚拟机 第三版》一书。

下一篇博客,我们会介绍对象再HostSpot虚拟机中如何创建,布局访问的,谢谢大家。

感谢大家的阅读,欢迎大家长按二维码关注,私信交流,转发,点在看,谢谢!

猜你喜欢

转载自blog.csdn.net/qiruibbb/article/details/106058234
今日推荐