您的位置 首页 java

一篇文章带你整理面试中JVM 的运行机制、多线程和 JVM 的相关知识点

文章目录

一、JVM 的运行机制

二、JVM 的内存区域

1. 程序计数器:线程私有,无内存溢出问题

2. 虚拟机栈:线程私有,描述Java方法的执行过程

3. 本地方法区:线程私有

4. 堆:也叫作运行时数据区,线程共享

5. 方法区:线程共享

一、JVM 的运行机制

JVM(Java Virtual Machine)是用于运行Java字节码的虚拟机,包括一套字节码指令集、一组程序寄存器、一个虚拟机栈、一个虚拟机堆、一个方法区和一个垃圾回收器。

JVM运行在操作系统之上,不与硬件设备直接交互。

各个区域具体的功能作用可参考:一篇文章带你学习 Java 虚拟机内存的各个区域

Java源文件在通过编译器之后被编译成相应的.Class文件(字节码文件),.Class文件又被JVM中的解释器编译成机器码在不同的操作系统(Windows、Linux、Mac)上运行。每种操作系统的解释器都是不同的,但基于解释器实现的虚拟机是相同的,这也是Java能够跨平台的原因。

在一个Java进程开始运行后,虚拟机就开始实例化了,有多个进程启动就会实例化多个虚拟机实例。进程退出或者关闭,则虚拟机实例消亡,在多个虚拟机实例之间不能共享数据。

Java程序的具体运行过程如下:

Java源文件被编译器编译成字节码文件。

JVM将字节码文件编译成相应操作系统的机器码。

机器码调用相应操作系统的本地方法库执行相应的方法。

Java虚拟机包括一个类加载器子系统(Class Loader SubSystem)、运行时数据区(Runtime Data Area)、执行引擎和本地接口库(Native Interface Library)。本地接口库通过调用本地方法库(Native Method Library)与操作系统交互,如图1-1所示。

一篇文章带你整理面试中JVM 的运行机制、多线程和 JVM 的相关知识点

其中:

类加载器子系统用于将编译好的.Class文件加载到JVM中;

运行时数据区用于存储在JVM运行过程中产生的数据,包括程序计数器、方法区、本地方法区、虚拟机栈和虚拟机堆;

执行引擎包括即时编译器和垃圾回收器,即时编译器用于将Java字节码编译成具体的机器码,垃圾回收器用于回收在运行过程中不再使用的对象;

本地接口库用于调用操作系统的本地方法库完成具体的指令操作。

二、JVM 的内存区域

JVM的内存区域分为线程私有区域(程序计数器、虚拟机栈、本地方法区)、线程共享区域(堆、方法区)和直接内存

一篇文章带你整理面试中JVM 的运行机制、多线程和 JVM 的相关知识点

线程私有区域的生命周期与线程相同,随线程的启动而创建,随线程的结束而销毁。在JVM内,每个线程都与操作系统的本地线程直接映射,因此这部分内存区域的存在与否和本地线程的启动和销毁对应。

线程共享区域随虚拟机的启动而创建,随虚拟机的关闭而销毁

直接内存也叫作堆外内存,它并不是JVM运行时数据区的一部分,但在并发编程中被频繁使用。

Java进程可以通过堆外内存技术避免在Java堆和Native堆中来回复制数据带来的资源占用和性能消耗,因此堆外内存在高并发应用场景下被广泛使用(Netty、Flink、HBase、Hadoop都有用到堆外内存)

各个区域具体的功能作用可参考:一篇文章带你学习 Java 虚拟机内存的各个区域

1. 程序计数器:线程私有,无内存溢出问题

程序计数器是一块很小的内存空间,用于存储当前运行的线程所执行的字节码的行号指示器

由于Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令

因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储

程序计数器属于“线程私有”的内存区域,它是唯一没有Out OfMemory(内存溢出)的区域

2. 虚拟机栈:线程私有,描述Java方法的执行过程

虚拟机栈是描述Java方法的执行过程的内存模型,它在当前栈帧(Stack Frame)中存储了局部变量表、操作数栈、动态链接、方法出口等信息。

栈帧用来记录方法的执行过程,在方法被执行时虚拟机会为其创建一个与之对应的栈帧,方法的执行和返回对应栈帧在虚拟机栈中的入栈和出栈。

无论方法是正常运行完成还是异常完成(抛出了在方法内未被捕获的异常),都视为方法运行结束。图1-3展示了线程运行及栈帧变化的过程。线程1在CPU1上运行,线程2在CPU2上运行,在CPU资源不够时其他线程将处于等待状态(如图3-1中等待的线程N),等待获取CPU时间片。

而在线程内部,每个方法的执行和返回都对应一个栈帧的入栈和出栈,每个运行中的线程当前只有一个栈帧处于活动状态

一篇文章带你整理面试中JVM 的运行机制、多线程和 JVM 的相关知识点

3. 本地方法区:线程私有

本地方法区和虚拟机栈的作用类似,区别是虚拟机栈为执行Java方法服务,本地方法栈为Native方法服务

4. 堆:也叫作运行时数据区,线程共享

在JVM运行过程中创建的对象和产生的数据都被存储在堆中,堆是被线程共享的内存区域,也是垃圾收集器进行垃圾回收的最主要的内存区域

由于现代JVM采用分代收集算法,因此Java堆从GC(GarbageCollection,垃圾回收)的角度还可以细分为:新生代、老年代和永久代。

关于新生代、老年代和永久代的理解:点击参考

5. 方法区:线程共享

方法区也被称为永久代,用于存储常量、静态变量、类信息、即时编译器编译后的机器码、运行时常量池等数据

一篇文章带你整理面试中JVM 的运行机制、多线程和 JVM 的相关知识点

JVM把GC分代收集扩展至方法区,即使用Java堆的永久代来实现方法区,这样JVM的垃圾收集器就可以像管理Java堆一样管理这部分内存。

永久代的内存回收主要针对常量池的回收和类的卸载,因此可回收的对象很少。

具体针对的是废弃的常量和无用的类

运行时常量池(Runtime Constant Pool)中存储的是符号引用和编译器生成的各种字面量,比如:String str = “Student”,str 就是符号引用,需要在运行期间进行解析,因为这个值不是固定的,可以变化,“Student” 就是一个字面量

字面量就是声明为 final 的常量值,符号引用比如全限定名、字段的名称和描述符,方法的名称和描述符,编译时无法确定它的内存地址,比如 str 编译时就会被编译成符号引用

运行时常量池只是一个JVM内存模型的一个概念,属于方法区下面,是一种定义,不是一种实现,也就是只要符合标准的Java虚拟机,那么这个虚拟机就应该有存放运行时常量池所定义的数据的功能。

具体实现是,字面量放到了 JVM 堆中,符号引用放到了本地内存。

静态变量也属于方法区的一部分。

在类信息(Class文件)中不但保存了类的版本、字段、方法、接口等描述信息,还保存了常量信息。

在即时编译后,代码的内容将在执行阶段(类加载完成后)被保存在方法区的运行时常量池中。

Java虚拟机对Class文件每一部分的格式都有明确的规定,只有符合JVM规范的Class文件才能通过虚拟机的检查,然后被装载、执行。

小伙伴们有兴趣想了解内容和更多相关学习资料的请点赞收藏+评论转发+关注我,后面会有很多干货。我有一些面试题、架构、设计类资料可以说是程序员面试必备!所有资料都整理到网盘了,需要的话欢迎下载!私信我回复【111】即可免费获取

一篇文章带你整理面试中JVM 的运行机制、多线程和 JVM 的相关知识点

版权声明:本文为CSDN博主「南淮北安」的原创文章,

原文链接:

文章来源:智云一二三科技

文章标题:一篇文章带你整理面试中JVM 的运行机制、多线程和 JVM 的相关知识点

文章地址:https://www.zhihuclub.com/180291.shtml

关于作者: 智云科技

热门文章

网站地图