01 JVM 从入门到实战 | JVM介绍

什么是 JVM

先来看下百度百科的解释:

JVM 是 Java Virtual Machine(Java 虚拟机)的缩写,JVM 是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

晦涩难懂有没有,简单理解就是说虚拟机是物理机的软件实现。

Java 的设计理念是 WORA(Write Once Run Anywhere,一次编写到处运行)。编译器将 Java 文件编译为 Java .class 文件,然后将 .class 文件输入到 JVM 中,JVM 执行类文件的加载和执行,最后转变成机器可以识别的机器码进行最终的操作。

为什么要学习 JVM

每个 Java 开发人员都知道字节码经由 JRE(Java 运行时环境)执行。但他们或许不知道 JRE 其实是由 Java 虚拟机(JVM)实现,JVM 分析字节码,解释并执行它。作为开发人员,了解 JVM的 架构是非常重要的,因为它使我们能够编写出更高效的代码。

但是 JVM 在帮我们实现 Write Once Run Anywhere 的同时,有利有弊,因为在这个过程中涉及到了内存管理,尤其是多线程情况下的内存管理问题,所以我们更应该学习 JVM 的知识来帮助自己写出更好的代码。

根据上边对 JVM 的概念介绍我们知道,JVM 的主要作用在于以下两方面,之后我们的介绍也会以此着手。

  • 软件层面的机器码翻译
  • 内存管理

最近也在学习《深入理解 Java 虚拟机》这本书,此处贴个书中的图过来:

图来自于《深入理解 Java 虚拟机 第 2 版》

下边就详细介绍一下这张图中的各个组件

运行时数据区

这个区域描述的是 Java 代码运行时的状态,是我们非常关注的一个状态-程序运行状态,因为我们写代码就是为了运行,不运行的状态对我们是没什么吸引力的。说白了 Java 代码不外乎 数据 指令 控制 这三类型语句,所以我们将 JVM 运行时数据区可以划分为如下两大类:

  • 数据
    • 方法区
    • 堆(Heap)
  • 指令
    • 虚拟机栈
    • 本地方法栈
    • 程序计数器
程序计数器

定义:指向当前线程正在执行的字节码指令的地址 也就是行号

注意:我们需要思考一个问题,我的当前线程本身已经在执行了,为什么还要找个寄存器把他的执行行号记录下来呢?

因为我们程序执行的最小单位是线程,而线程在 CPU 上执行的时候是抢占式的,这样的话就存在线程被挂起的情况,例如:有 A B 两个线程,如果 A 线程执行过程被 B 线程抢占了 CPU,则需要把挂起的 A 线程 当前执行到的行号存储下来,等到 A 重新获得 CPU 时间片执行权的时候去程序计数器获得上一次执行的行号以便于继续执行这个程序。

所以,每个线程都有自己的 程序计数器,而且是互不干扰的,属于线程私有区域

  • 如果执行的是一个 Java 方法,计数器记录的是正在执行的虚拟机字节码指令的地址
  • 如果执行的是一个 Native 方法,计数器的值则为空(undefined)
虚拟机栈

定义:存储当前线程运行方法所需要的数据、指令和返回地址,生命周期与线程相同,同样属于线程私有区域

每个 Java 方法在执行的同时都会创建一个栈帧用于存储局部变量、操作数栈、方法出口等信息,

如下所示,这个栈帧会存储的信息包括:

  • 局部变量表

  • 操作数栈

  • 动态链接

  • 出口

  • … …

每一个方法从调用直至执行完成的过程,其实真正对应的是一个栈帧在虚拟机栈中入栈到出栈的过程。

其中局部变量表存放了编译器可知的各种基本数据类型、引用对象等。需要注意的是因为局部变量表空间长度只有 32 位,如果是 long 和 double 类型的话会占用 2 个局部变量表空间,其他数据类型只占用 1 个。

注意:局部变量表所需的内存空间在编译期间就会车队分配完成,因为在进入一个方法时,这个方法需要在栈帧中分配多大的局部空间是完全确定的,方法运行期间局部变量大小是不会改变的。

本地方法栈

和虚拟机栈类似,只不过他存储的是当前线程调用的本地方法所需要的数据、指令和返回地址等,本地方法时标识有 Native 关键字的方法,此处就不展开描述了,参考上述虚拟机栈的介绍。

另外,根据《深入理解 Java 虚拟机》这本书的介绍,有些虚拟机(如 Sun HotSpot 虚拟机)直接就把本地方法栈和虚拟机栈合二为一了。

方法区

这块区域属于线程共享群与,主要存储的信息包括已被虚拟机你加载的类信息(类的元信息)、常量、静态变量、JIT(编译器编译后的代码)等数据。

方法区有一块区域我们称之为 运行时常量池,存放编译期生成的各种字面量和符号引用,运行时常量池有一个重要特征是具备动态性,也就是说在运行期间依然可以将新的常量放入池中,我们开发常用的有 String 类的 intern() 方法

堆(Heap)

属于线程共享区域,在虚拟机启动时创建,是虚拟机管理的内存中最大的一块。它的唯一作用就是存放对象实例。

根据虚拟机规范的描述是:所有的对象实例及数组都要在堆上分配。当然随着现在技术的发展优化这个也变得没有那么绝对,后续会进行分享。

这块区域也是垃圾收集器管理的主要区域,现如今流行的垃圾回收器基本都采用的是分代收集算法,所以也就衍生了一些分代方式,

比如对于内存模型的划分,在 JDK1.8 以前的版本基本是这样的:

在这里插入图片描述

  • 新生代

    • Eden
    • s0
    • s1
  • 老年代

  • 永久代

在 JDK 1.8 以后的版本:

在这里插入图片描述

  • 新生代

  • 老年代

  • Meta Space

此处小提一下,之所以在 JDK 1.8 以后 有了 Meta Space,其设计的目的在于规避永久代溢出的问题,因为 Meta Space 是可以自动扩容的,就跟 Java 中的集合一样。

以上种种的划分方式,都是为了更好地回收内存或者分配内存,从下一篇开始就开始学习内存分配及垃圾回收相关算法啦!

总结

  • JVM 负责软件层面的机器码翻译,可以把我们写的 .java 文件翻译成机器可以识别的机器码
  • JVM 负责内存管理
  • JVM 的运行时数据区包括方法区、堆、虚拟机栈、本地方法栈和程序计数器
  • JVM 中的方法区和堆区是所有线程共享的,其他区域都是线程独享的

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
大学生参加学科竞赛有着诸多好处,不仅有助于个人综合素质的提升,还能为未来职业发展奠定良好基础。以下是一些分析: 首先,学科竞赛是提高专业知识和技能水平的有效途径。通过参与竞赛,学生不仅能够深入学习相关专业知识,还能够接触到最新的科研成果和技术发展趋势。这有助于拓展学生的学科视野,使其对专业领域有更深刻的理解。在竞赛过程中,学生通常需要解决实际问题,这锻炼了他们独立思考和解决问题的能力。 其次,学科竞赛培养了学生的团队合作精神。许多竞赛项目需要团队协作来完成,这促使学生学会有效地与他人合作、协调分工。在团队合作中,学生们能够学到如何有效沟通、共同制定目标和分工合作,这对于日后进入职场具有重要意义。 此外,学科竞赛是提高学生综合能力的一种途径。竞赛项目通常会涉及到理论知识、实际操作和创新思维等多个方面,要求参赛者具备全面的素质。在竞赛过程中,学生不仅需要展现自己的专业知识,还需要具备创新意识和解决问题的能力。这种全面的综合能力培养对于未来从事各类职业都具有积极作用。 此外,学科竞赛可以为学生提供展示自我、树立信心的机会。通过比赛的舞台,学生有机会展现自己在专业领域的优势,得到他人的认可和赞誉。这对于培养学生的自信心和自我价值感非常重要,有助于他们更加积极主动地投入学习和未来的职业生涯。 最后,学科竞赛对于个人职业发展具有积极的助推作用。在竞赛中脱颖而出的学生通常能够引起企业、研究机构等用人单位的关注。获得竞赛奖项不仅可以作为个人履历的亮点,还可以为进入理想的工作岗位提供有力的支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值