大连网站建设方法,一个云主机 多个网站,软件开发工程师前景,海口什么网站建设文章目录 一、JVM 运行流程二、虚拟机栈#xff08;线程私有#xff09;三、本地方法栈 #xff08;线程私有#xff09;四、方法区#xff08;元数据区#xff09;五、堆#xff08;线程共享#xff09;六、程序计数器#xff08;线程私有#xff09; 一、JVM 运行流… 文章目录 一、JVM 运行流程二、虚拟机栈线程私有三、本地方法栈 线程私有四、方法区元数据区五、堆线程共享六、程序计数器线程私有 一、JVM 运行流程
JVM 是 Java 程序的运行基础和运行环境同时也是 Java 实现 一次编译到处运行 的关键所在。因此深入了解 JVM 对于学习和理解 Java 编程语言是至关重要的那么JVM 到底是如何运行的呢
下面这张图片展示了 JVM 的基本运行过程
JVM的执行过程涉及以下主要组成部分 Java 代码转换为字节码.class文件编写完成后的 Java 源代码需要通过 Java 编译器编译为字节码生成 .class 文件这些文件包含了 Java 程序的中间代码。 类加载器ClassLoaderJVM 的类加载器负责将 .class 字节码文件加载到内存中的运行时数据区。类加载器根据类的全限定名Fully Qualified Name查找并加载对应的字节码文件并根据文件内容创建 Class 对象来代表这个类以供后续的执行引擎调用。 运行内存管理JVM负责管理程序运行时的内存区域主要包括以下几个区域 虚拟机栈Java Virtual Machine Stacks用于存储方法调用的栈帧包含局部变量、操作数栈等。本地方法栈Native Method Stacks用于执行本地方法的栈。方法区Method Area存储类的结构信息、常量池、静态变量等。堆Heap存储对象实例和数组的内存区域。程序计数器Program Counter记录当前线程执行的字节码指令的地址或索引。 执行引擎Execution Engine执行引擎是 JVM 的核心组件之一负责执行加载到内存中的字节码文件。执行引擎有两种方式执行字节码 解释执行逐条解释字节码指令并执行相应的操作。解释执行效率较低但跨平台性好适用于刚开始执行的代码段或是执行次数较少的代码段。编译执行将字节码编译成特定平台的本地代码然后交由CPU执行。编译执行效率高但需要额外的编译时间适用于执行次数频繁的代码段。 本地方法库Native LibrariesJVM 中的 Java 代码无法直接访问底层操作系统因为字节码只是一套跨平台的指令集规范。当 Java 代码需要执行底层操作系统或与本地代码如C、C进行交互的时候。就需要通过本地方法库来实现这些功能。本地方法库允许 Java 程序调用与操作系统相关的本地代码从而实现整个程序的功能。
以上就是 JVM 的主要运行过程以及运行时涉及的组成部分下文是对其中的运行时数据区的详细介绍。
二、虚拟机栈线程私有
虚拟机栈是 Java 线程私有的内存区域用于存储线程的方法调用和局部变量等信息。虚拟机栈的生命周期和线程相同虚拟机栈描述的是 Java 方法执行的内存模型
每个方法在执行的时候都会创建一个栈帧Stack Frame用于存储局部变量表、操作数栈、动态链接、方法出口等信息。当方法结束的时候响应的栈帧也会因为出栈而销毁。
虚拟机栈主要包含四个部分 局部变量表用于存储方法的局部变量。在Java方法执行时它会分配一块内存区域用于存储方法参数和方法内部定义的局部变量。局部变量表的大小在编译期间就被确定存储的数据类型包括基本数据类型和对象引用。操作栈用于执行方法时的计算操作。Java虚拟机的字节码指令通常都是基于操作数栈进行运算的。当一个方法被调用时会将参数值和返回值等压入操作数栈在方法执行时字节码指令会从操作数栈中取值进行计算。动态链接用于指向运行时常量池中该栈帧所属方法的引用。在Java虚拟机的运行时常量池中存放着每个类的方法的符号引用动态链接将这些符号引用与实际内存地址进行关联。方法返回地址用于指示方法的返回地址。当方法执行完成后需要知道从哪里继续执行方法返回地址就是用来记录返回的目标地址。 什么是线程私有 线程私有是指在多线程环境下每个线程都拥有自己独立的内存区域其他线程无法直接访问或修改该区域。线程私有内存中的数据对于每个线程都是独立的互不影响。这种设计使得多线程程序可以同时执行每个线程都能够独立地运行和维护自己的数据。在JVM中虚拟机栈、本地方法栈和程序计数器是线程私有的内存区域。每个线程都有自己的虚拟机栈和本地方法栈用于支持方法调用和执行并且这些栈的数据对其他线程是不可见的。程序计数器也是线程私有的每个线程都有自己的程序计数器用于指示当前线程执行的字节码指令的地址或索引。线程的切换时会保存和恢复程序计数器的值以保证线程切换后能正确继续执行。其他共享内存区域如堆和方法区元空间是线程共享的。堆用于存储Java对象实例和数组方法区用于存储类的结构信息、常量池、静态变量等。多个线程可以共同访问堆和方法区的数据因此在多线程环境下需要通过同步机制来保护共享数据的一致性和正确性。内存区域按线程私有和共享的划分 三、本地方法栈 线程私有
本地方法栈与虚拟机栈类似也是 Java 线程私有的内存区域。它用于支持 Java 程序调用和执行本地方法Native Method。本地方法是使用其他语言如C、C编写的方法通过本地方法接口JNIJava Native Interface与 Java 代码进行交互。本地方法栈和虚拟机栈在功能上也是类似的但它们分别用于 Java 方法和本地方法的调用。
四、方法区元数据区
方法区是 Java 线程共享的内存区域用于存储类的结构信息、常量池、静态变量、即时编译器编译后的代码等。在 JDK 8 及以前版本方法区是永久代Permanent Generation而在 JDK 8 及以后版本方法区被替换为元空间Metaspace。方法区的大小可以通过启动JVM 时的参数来设置。 在《Java虚拟机规范中》把此区域称之为 “方法区”而在 HotSpot 虚拟机的实现中在 JDK 7 时此区域叫做永久代Permanent GenerationJDK 8 中叫做元空间Metaspace。 JDK 1.8 元空间的变化 对于 HotSpot 来说JDK 8 元空间的内存属于本地内存这样元空间的大小就不在受 JVM 最大内存的参数影响了而是与本地内存的大小有关。JDK 8 中将字符串常量池移动到了堆中。 运行时常量池 运行时常量池Runtime Constant Pool是方法区的一部分用于存放在编译期间生成的各种字面量和符号引用。它是在类加载过程中由虚拟机根据字节码文件中的常量池表构建而成。
在运行时常量池中主要包含两种类型的数据 字面量 字符串字面量即Java程序中直接写的字符串值例如“Hello, Java”。在JDK 8中字符串字面量被移动到堆中这样它们也可以被垃圾收集器回收避免了一些内存问题。final常量在编译期间可以确定的final常量例如final int MAX_VALUE 100;。基本数据类型的值例如整数、浮点数、字符等基本数据类型的字面量。 符号引用 符号引用是一种在编译期间产生的、但在类加载阶段需要用到的一种数据结构它用于描述被引用的目标。符号引用包括 类和接口的完全限定名例如java.lang.String。字段的名称和描述符用于描述字段的名称和数据类型例如int count。方法的名称和描述符用于描述方法的名称和参数列表以及返回值类型例如void print(String message)。
运行时常量池在类加载后会存放在方法区中并且在整个类的生命周期中存在与类本身一起被回收。它在程序运行时提供了常量池解析、动态链接、方法调用等功能为 Java 程序的执行提供了必要的支持。
五、堆线程共享
堆Heap是 Java 虚拟机中的一个运行时数据区域用于存储Java对象实例和数组。堆是JVM中最大的一块内存区域也是唯一被所有线程共享的内存区域。
在Java程序运行过程中当使用new关键字创建对象时对象实例会被分配在堆中。同时数组也是对象因此数组的元素也会存储在堆中。堆的大小可以在启动 JVM 时通过参数来指定也可以动态调整如果未指定大小则JVM会根据系统内存自动设置初始大小。
堆的主要特点包括 线程共享堆是所有线程共享的内存区域。所有线程都可以访问堆中的对象实例这使得多个线程可以共同操作和共享对象。 动态分配和回收堆的大小在程序运行时是可以动态调整的。当创建对象时JVM会自动分配堆中的内存空间。当对象不再被引用时垃圾收集器会自动回收堆中不再使用的对象的内存。 垃圾回收堆中的内存由垃圾收集器负责管理。垃圾收集器会周期性地检查堆中的对象将不再被引用的对象标记为垃圾然后回收这些垃圾对象的内存释放给堆供其他对象使用。 自动内存管理Java 中的堆内存由 JVM 自动进行内存管理程序员不需要手动释放内存。JVM 会自动进行垃圾回收释放不再使用的内存避免了内存泄漏等问题。
另外在Java虚拟机的堆内存中通常被划分为两个主要区域新生代Young Generation和老生代Old Generation。 新生代Young Generation 新生代是存放新创建的对象的区域。在新生代中通常会将堆内存划分为一个Eden空间和两个Survivor空间通常称为S0和S1。新创建的对象首先会被分配到Eden空间。当Eden空间满时会触发Minor GCYoung GC垃圾回收器会将Eden空间和其中还存活的对象复制到一个未使用的Survivor空间中。然后垃圾回收器会清除Eden空间和正在使用的Survivor空间将其中的垃圾对象回收。幸存下来的对象会被晋升到老生代。 老生代Old Generation 老生代是存放长时间存活的对象的区域。老生代中存放的对象通常是在新生代经过一定次数的Minor GC后仍然存活的对象或者是大对象等。当老生代空间满时会触发Major GCFull GC垃圾回收器会对整个堆进行回收包括新生代和老生代的所有对象。
通过将堆内存分为新生代和老生代并采用不同的垃圾回收策略可以提高垃圾回收的效率。新生代中的Minor GC频繁进行回收生命周期短的对象尽可能快速地释放内存而老生代中的Major GC则相对较少进行回收生命周期长的对象保证了老生代的稳定性和可靠性。
六、程序计数器线程私有
程序计数器也是 Java 线程私有的内存区域。它是一种指示器用于指示当前线程执行的字节码指令的地址或索引。在Java线程切换时程序计数器的值会被保存和恢复以保证线程切换后能正确继续执行。