简述JVM(1)——类加载器和运行时数据区(下)

简介: 简述JVM(1)——类加载器和运行时数据区(下)

1.2 运行时数据区

JVM 的运行时数据区,不同虚拟机实现可能略微有所不同,但都会遵从 Java 虚拟机规范,Java 8 虚拟机规范规定,Java 虚拟机所管理的内存将会包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区。


1.2.1 程序计数器(Program Counter Register)

JVM 中的程序计数寄存器中的 Register 命名源于CPU 的寄存器,寄存器存储指令相关的现场信息。这里,并非是广义上所指的物理寄存器,或许将其翻译为 PC 计数器(或指令计数器)会更加贴切(也称为程序钩子)。JVM 中的PC 寄存器是对物理 PC 寄存器的一种抽象模拟。


1.它是一块很小的内存空间,几乎可以忽略不计,但是它是运行速度最快的存储区域。

2.在 JVM 规范中,每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程生命周期保持一致。

3.任何时间一个线程都只有一个方法在执行,也就是所谓的当前方法,程序计数器会存储当前线程正在执行的Java方法的JVM指令地址。如果是在执行native方法,则是未指定值(undefined)。

4.它是程序控制流的指示器,分支、循环、跳转、异常处理,线程恢复等基础功能都需要依赖这个计数器来完成。

5.字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。

6.它是唯一一个在Java虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域.


那么为什么使用程序计数器记录当前线程的执行地址呢?我们都知道CPU需要不停的切换各个线程,这时候从其他线程切换到这个线程时,根据程序计数器就知道该从哪里开始执行了。JVM 的字节码解释器就需要通过改变程序计数器的值来明确下一条应该执行什么样的字节码指令,由于这个原因,必须为每个线程分配一个程序计数器,这样各个线程就可以互不干扰。


1.2.2 Java 虚拟机栈(Java Virtual Machine Stacks)

首先我们需要学会区分栈和堆:

栈是运行时的单位,而堆时存储的单位。

 1.栈:解决程序的运行问题,即程序如何执行,或者说如何处理数据。

 2.堆:解决的是数据存储的问题,即数据怎么放,放在哪儿。


1.2.2.1 Java虚拟机栈概述

每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个栈帧,对应着一次方法的调用。Java 虚拟机栈是线程私有的。生命周期和线程一致。栈中的数据都以栈帧为单位存储。栈帧是一个内存区块,是一个数据集,维系着方法执行过程中的各种数据信息。


作用:主要负责Java程序的运行,保存方法内的局部变量,还有部分结果,还参与方法的调用和返回。

image.png



栈是一种快速有效的分配存储方式,访问速度仅次于程序计数器。JVM 直接对 Java 栈的操作只有两个:

     1.调用方法,进栈。

     2.执行结束后出栈。

注意:对于栈来说不存在垃圾回收问题。


1.2.2.2栈的运行原理

1.JVM 直接对 Java 栈的操作只有两个,就是对栈帧的压栈和出栈,遵循”先进后出“后进先出的原则。

2.在一条活动的线程中,一个时间点上,只会有一个活动栈。即只有当前在执行的方法的栈帧(此时这个栈桢在栈顶)是有效地,这个栈帧被称为当前栈(Current Frame),与当前栈帧对应的方法称为当前方法(Current Method),定义这个方法的类称为当前类(Current Class)。

3.执行引擎运行的所有字节码指令只针对当前栈帧进行操作。

4.如果在该方法中调用了其他方法,对应的新的栈帧就会被创建出来,放在栈的顶端,成为新的当前栈帧。

5.不同线程中所包含的栈帧(方法)是不允许存在相互引用的,即不可能在一个栈中引用另一个线程的栈帧(方法)。

6.如果当前方法调用了其他方法,方法返回之际,当前栈帧会传回此方法的执行结果给前一个栈帧,接着虚拟机会丢弃当前栈帧,使得前一个栈帧重新成为当前栈帧。Java 方法有两种返回的方式,一种是正常的函数返回,使用 return 指令,另一种是抛出异常。不管哪种方式,都会导致栈帧被弹出.

image.png


1.2.2.3 栈帧的内部

每个栈帧中都有:局部变量表、操作数栈、动态链接、方法返回地址、一下附加信息。


1.2.2.3.1局部变量表(Local Variables)

局部变量表用于存放方法参数和方法内部定义的局部变量。

对于基本数据类型的变量,则直接存储它的值,对于引用类型的变量,则存的是指向对象的引用。


1.2.2.3.2 操作数栈(Operand Stack)(或表达式栈)

程序中的所有计算过程都是在借助于操作数栈来完成的。


1.2.2.3.3 动态链接(Dynamic Linking) (或指向运行时常量池的方法引用)

因为在方法执行的过程中有可能需要用到类中的常量或方法,所以必须要有一个引用指向运行时常量池。


1.2.2.3.4 方法返回地址(Return Address)(或方法正常退出或者异常退出的定义)

当一个方法执行完毕之后,要返回之前调用它的地方,因此在栈帧中必须保存一个方法返回地址。


1.2.2.3.5 一些附加信息

例如和调试相关的信息,这部分信息完全取决于不同的虚拟机实现。


1.2.3 本地方法栈

1.Java 虚拟机栈管理 java 方法的调用,而本地方法栈用于管理本地方法的调用。

2.本地方法栈也是线程私有的.

3.允许被实现成固定或者是可动态扩展的内存大小。


内存溢出方面也是相同的。

如果线程请求分配的栈容量超过本地方法栈允许的最大容量,会抛出StackOverflowError。

如果本地方法可以动态扩展,并在扩展时无法申请到足够的内存会抛出OutOfMemoryError。


4.本地方法是用 C 语言写的.


1.2.4 堆内存

堆内存概述:

1.一个 JVM 实例只存在一个堆内存,堆也是 Java 内存管理的核心区域。

2.Java 堆区在 JVM 启动时的时候即被创建,其空间大小也就确定了,是 JVM 管理的最大一块内存空间。

3.堆内存的大小是可以调节。一般情况可以将起始值和最大值设置为一致,这样会减少垃圾回收之后堆内存重新分配大小的次数,提高效率。

4.《Java 虚拟机规范》规定,堆可以处于物理上不连续的内存空间中,但逻辑上它应该被视为连续的。

5.所有的线程共享 Java 堆,在这里还可以划分线程私有的缓冲区。

6.《Java 虚拟机规范》中对 Java 堆的描述是:所有的对象实例都应当在运行时分配在堆上。

7.在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除。

8.堆是 GC(Garbage Collection,垃圾收集器)执行垃圾回收的重点区域。

我们在之后在细讲堆内存的区域划分及垃圾回收机制


1.2.5方法区

方法区,是一个被线程共享的内存区域。其中主要存储加载的类字节码、class/method/field 等元数据、static final 常量、static 变量、编译器编译后的代码等数据。另外,方法区包含了一个特殊的区域“运行时常量池”。

Java 虚拟机规范中明确说明:”尽管所有的方法区在逻辑上是属于堆的一部分,但对于HotSpotJVM 而言,方法区还有一个别名叫做 Non-Heap(非堆),目的就是要和堆分开。所以,方法区看做是一块独立于 Java 堆的内存空间。方法区在 JVM 启动时被创建,并且它的实际的物理内存空间中和 Java 堆区一样都可以是不连续的。

方法区的大小:跟堆空间一样,可以选择固定大小或者可扩展。

方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出的错误 :java.lang.OutOfMemoryError:Metaspace。

image.png



结语


这次就先写这些,文中如果存在不对的地方,欢迎各位读者批评指正。我会在今后更新本地方法接口、执行引擎和垃圾回收机制等相关内容,如果大家感兴趣,可以关注博主,我们一起交流学习。


目录
相关文章
|
1月前
|
Java
jvm复习,深入理解java虚拟机一:运行时数据区域
这篇文章深入探讨了Java虚拟机的运行时数据区域,包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区、元空间和运行时常量池,并讨论了它们的作用、特点以及与垃圾回收的关系。
62 19
jvm复习,深入理解java虚拟机一:运行时数据区域
|
1月前
|
存储 SQL 小程序
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
这篇文章详细介绍了Java虚拟机(JVM)的运行时数据区域和JVM指令集,包括程序计数器、虚拟机栈、本地方法栈、直接内存、方法区和堆,以及栈帧的组成部分和执行流程。
31 2
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
|
2月前
|
安全 Java 应用服务中间件
JVM常见面试题(三):类加载器,双亲委派模型,类装载的执行过程
什么是类加载器,类加载器有哪些;什么是双亲委派模型,JVM为什么采用双亲委派机制,打破双亲委派机制;类装载的执行过程
JVM常见面试题(三):类加载器,双亲委派模型,类装载的执行过程
|
1月前
|
Arthas 监控 Java
JVM知识体系学习七:了解JVM常用命令行参数、GC日志详解、调优三大方面(JVM规划和预调优、优化JVM环境、JVM运行出现的各种问题)、Arthas
这篇文章全面介绍了JVM的命令行参数、GC日志分析以及性能调优的各个方面,包括监控工具使用和实际案例分析。
43 3
|
1月前
|
缓存 前端开发 Java
JVM知识体系学习二:ClassLoader 类加载器、类加载器层次、类过载过程之双亲委派机制、类加载范围、自定义类加载器、编译器、懒加载模式、打破双亲委派机制
这篇文章详细介绍了JVM中ClassLoader的工作原理,包括类加载器的层次结构、双亲委派机制、类加载过程、自定义类加载器的实现,以及如何打破双亲委派机制来实现热部署等功能。
42 3
|
2月前
|
Arthas Java 测试技术
JVM —— 类加载器的分类,双亲委派机制
类加载器的分类,双亲委派机制:启动类加载器、扩展类加载器、应用程序类加载器、自定义类加载器;JDK8及之前的版本,JDK9之后的版本;什么是双亲委派模型,双亲委派模型的作用,如何打破双亲委派机制
JVM —— 类加载器的分类,双亲委派机制
|
1月前
|
前端开发 Java 应用服务中间件
JVM进阶调优系列(1)类加载器原理一文讲透
本文详细介绍了JVM类加载机制。首先解释了类加载器的概念及其工作原理,接着阐述了四种类型的类加载器:启动类加载器、扩展类加载器、应用类加载器及用户自定义类加载器。文中重点讲解了双亲委派机制,包括其优点和缺点,并探讨了打破这一机制的方法。最后,通过Tomcat的实际应用示例,展示了如何通过自定义类加载器打破双亲委派机制,实现应用间的隔离。
|
3月前
|
数据库 C# 开发者
WPF开发者必读:揭秘ADO.NET与Entity Framework数据库交互秘籍,轻松实现企业级应用!
【8月更文挑战第31天】在现代软件开发中,WPF 与数据库的交互对于构建企业级应用至关重要。本文介绍了如何利用 ADO.NET 和 Entity Framework 在 WPF 应用中访问和操作数据库。ADO.NET 是 .NET Framework 中用于访问各类数据库(如 SQL Server、MySQL 等)的类库;Entity Framework 则是一种 ORM 框架,支持面向对象的数据操作。文章通过示例展示了如何在 WPF 应用中集成这两种技术,提高开发效率。
58 0
|
3月前
|
C# UED 开发者
WPF动画大揭秘:掌握动画技巧,让你的界面动起来,告别枯燥与乏味!
【8月更文挑战第31天】在WPF应用开发中,动画能显著提升用户体验,使其更加生动有趣。本文将介绍WPF动画的基础知识和实现方法,包括平移、缩放、旋转等常见类型,并通过示例代码展示如何使用`DoubleAnimation`创建平移动画。此外,还将介绍动画触发器的使用,帮助开发者更好地控制动画效果,提升应用的吸引力。
173 0
|
1月前
|
存储 安全 Java
jvm 锁的 膨胀过程?锁内存怎么变化的
【10月更文挑战第3天】在Java虚拟机(JVM)中,`synchronized`关键字用于实现同步,确保多个线程在访问共享资源时的一致性和线程安全。JVM对`synchronized`进行了优化,以适应不同的竞争场景,这种优化主要体现在锁的膨胀过程,即从偏向锁到轻量级锁,再到重量级锁的转变。下面我们将详细介绍这一过程以及锁在内存中的变化。
37 4