端着JAVA语言开发的饭碗,却不知道JVM工作原理,你该“挨打“了!!!

端着JAVA语言开发的饭碗,却不知道JVM工作原理,你该"挨打"了!!!

什么是JVM

JVM的全称是Java Virtual Machine(Java虚拟机),它通过模拟一个计算机来达到计算机所具有的计算功能。实际上就是用来执行java字节码(二进制的形式)的虚拟计算机。它运行在操作系统之上,与硬件没有任何关系。

跨平台

『一次编译,到处运行』相信这句话大家并不陌生,可能经常还会犹然在耳,这句话就充分体现了jvm的跨平台的特性。
大家都知道JAVA的运行与平台无关,它的这个无关恰恰是跨平台性的体现,那么为什么能做到跨平台呢?实际上是因为JVM中的解释器负责将字节码解释成本地的机器码,这样就实现了JAVA语言的跨平台性,即程序在一次编写之后,可以到处执行,而不用再绑定到固定的机器或者机型。

JVM体系结构

JVM的结构基本上由四部分组成:

  • 类加载器(ClassLoader):在JVM启动时或运行时将需要的class加载到JVM中。
  • 执行引擎:负责执行class文件中包含的字节码命令,相当于实际机器上的CPU。执行引擎也就是执行一条条代码的流程,而代码都包含在方法内,所以执行引擎本质上就是执行一个个方法所串起来的流程,也就是对应我们通常所说的Java线程,每个Java线程就是一个执行引擎的实例。
  • 内存区:将内存区划分成若干个区以模拟实际机器上的存储、记录和调度功能模块,如实际机器上的各种功能的寄存器或者PC指针的记录器等。执行引擎在执行一段程序时需要存储一些东西,如操作码需要的操作结果、操作数等,这些都保存在内存中。
  • 本地方法调用:调用C或C++实现的本地方法的代码返回结果等。
    在这里插入图片描述

入口是编译好的字节码文件 -->经过类加载子系统(将我们的字节码加载到内存当中,生成一个class对象,中间经过三步:加载—>链接—>初始化)。

程序计数器:内存空间小,线程私有。字节码解释器工作是就是通过改变这个计数器的值来选取下一条需要执行指令的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖计数器完成。如果线程正在执行一个 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是 Native 方法,这个计数器的值则为 (Undefined)。此内存区域是唯一一个在 Java 虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域。

Java 虚拟机栈:线程私有,生命周期和线程一致。描述的是 Java 方法执行的内存模型:每个方法在执行时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行结束,就对应着一个栈帧从虚拟机栈中入栈到出栈的过程。局部变量表:存放了编译期可知的各种基本类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型)和 returnAddress 类型(指向了一条字节码指令的地址)。
StackOverflowError:线程请求的栈深度大于虚拟机所允许的深度。
OutOfMemoryError:如果虚拟机栈可以动态扩展,而扩展时无法申请到足够的内存。

本地方法栈:区别于 Java 虚拟机栈的是Java 虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。也会有 StackOverflowError 和 OutOfMemoryError 异常。

Java 堆:对于绝大多数应用来说,这块区域是 JVM 所管理的内存中最大的一块。线程共享,主要是存放对象实例和数组。内部会划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB)。可以位于物理上不连续的空间,但是逻辑上要连续。OutOfMemoryError:如果堆中没有内存完成实例分配,并且堆也无法再扩展时,抛出该异常。

方法区:属于共享内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

现在用一张图来介绍每个区域存储的内容:
在这里插入图片描述
在内存中,多个对象共享内存的是方法区和堆区(多个线程共享区)。
高级语言翻译为机器指令,主要是由执行引擎完成的。
解释器:解释运行,把字节码翻译为机器指令。
JIT即时编译器:主要是把热点代码缓存起来。

JVM工作机制

通常一个程序从编写到执行会经历以下一些阶段:
在这里插入图片描述
除了源代码和最后的可执行程序,中间的所有环节都是由现代意义上的编译器统一完成的。

JVM的生命周期

  • 虚拟机的启动
    Java虚拟机的启动是通过引导类加载器bootstrap class loader创建一个初始类initail calss来完成的,这个类是由虚拟机的具体实现指定的。
  • 虚拟机的执行
    一个运行着的java虚拟机有着清晰的任务,执行java程序。
    程序开始执行虚拟机就运行,程序结束虚拟机就结束运行。
    执行一个java程序的时候,真真正正的在执行的是一个java虚拟机进程
  • 虚拟机的停止:以下几种情况会退出虚拟机
    - 程序正常执行结束。
    - 程序在运行过程中遇到错误或者异常而终止执行。
    - 由于操作系统发生故障而导致虚拟机进程终止运行。
    - 某一个线程调用了Runtime类或者system类的exit方法或者调用runtime类的halt方法,并且java安全管理器允许执行安全退出的方法。

执行引擎的架构设计

每当创建一个新的线程时,JVM会为这个线程创建一个Java栈并分配一个PC计数器,PC计数器会指向这个线程的第一行可执行代码。每当执行一个新的方法时,JVM会为这个栈分配一个新的栈帧,栈帧包含了对应方法的参数、内部变量、执行结果等。在这里插入图片描述

执行引擎的执行过程

在这里插入图片描述
看上图的这段代码,我们来对main方法中的字节码指令做一个分析(其实就是使用javap -c 类名):在这里插入图片描述
在这里插入图片描述
代码执行之前,计数器的指针指向第一条指令的地址(偏移量0),局部变量区和操作栈都没有数据。代码开始执行之后,根据指令开始对操作栈和局部变量区进行运算。

注意: 当main方法中引用了其他方法时,则会创建新的栈帧,计数器仍旧只有一个,当执行到引用方法时,执行引擎会创建一个新的栈帧,而计数器保存的是当前栈帧的第一条指令地址,所以值是0。当执行完return指令时,整个方法对应的栈帧也将撤销,如果当前线程对应的Java栈中没有栈帧,那么这个Java栈也将被JVM撤销,整个JVM退出。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值