简述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



结语


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


目录
相关文章
|
10天前
|
监控 算法 Java
Java虚拟机(JVM)使用多种垃圾回收算法来管理内存,以确保程序运行时不会因为内存不足而崩溃。
【6月更文挑战第20天】Java JVM运用多种GC算法,如标记-清除、复制、标记-压缩、分代收集、增量收集、并行收集和并发标记,以自动化内存管理,防止因内存耗尽导致的程序崩溃。这些算法各有优劣,适应不同的性能和资源需求。垃圾回收旨在避免手动内存管理,简化编程。当遇到内存泄漏,可以借助VisualVM、JConsole或MAT等工具监测内存、生成堆转储,分析引用链并定位泄漏源,从而解决问题。
22 4
|
12天前
|
算法 Java
Java垃圾回收(Garbage Collection,GC)是Java虚拟机(JVM)的一种自动内存管理机制,用于在运行时自动回收不再使用的对象所占的内存空间
【6月更文挑战第18天】Java的GC自动回收内存,包括标记清除(产生碎片)、复制(效率低)、标记整理(兼顾连续性与效率)和分代收集(区分新生代和老年代,用不同算法优化)等策略。现代JVM通常采用分代收集,以平衡性能和内存利用率。
36 3
|
17天前
|
存储 缓存 安全
JVM(三)-运行时数据区(栈、程序计数器)
JVM(三)-运行时数据区(栈、程序计数器)
13 2
|
2天前
|
安全 前端开发 Java
《JVM由浅入深学习【一】 》JVM由简入深学习提升(类加载过程+父子类加载过程+类加载器+双亲委派机制)
《JVM由浅入深学习【一】 》JVM由简入深学习提升(类加载过程+父子类加载过程+类加载器+双亲委派机制)
7 0
|
26天前
|
存储 Java 编译器
【JavaEE初阶】 JVM 运行时数据区简介
【JavaEE初阶】 JVM 运行时数据区简介
|
6天前
|
存储 Java C++
Java虚拟机(JVM)在执行Java程序时,会将其管理的内存划分为几个不同的区域
【6月更文挑战第24天】Java JVM管理内存分7区:程序计数器记录线程执行位置;虚拟机栈处理方法调用,每个线程有独立栈;本地方法栈服务native方法;Java堆存储所有对象实例,垃圾回收管理;方法区(在Java 8后变为元空间)存储类信息;运行时常量池存储常量;直接内存不属于JVM规范,通过`java.nio`手动管理,不受GC直接影响。
16 5
|
4天前
|
存储 Java 对象存储
jvm内存模型剖析
当线程cpu时间片执行完后,线程进入休眠状态,当再次唤醒时,通过程序计数器确定指令执行到哪一行,然后继续往下执行。
16 1
|
6天前
|
存储 Java C++
Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据
Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据,如局部变量和操作数;本地方法栈支持native方法;堆存放所有线程的对象实例,由垃圾回收管理;方法区(在Java 8后变为元空间)存储类信息和常量;运行时常量池是方法区一部分,保存符号引用和常量;直接内存非JVM规范定义,手动管理,通过Buffer类使用。Java 8后,永久代被元空间取代,G1成为默认GC。
17 2
|
17天前
|
算法 安全 Java
JVM系列4-垃圾收集器与内存分配策略(二)
JVM系列4-垃圾收集器与内存分配策略(二)
23 0
JVM系列4-垃圾收集器与内存分配策略(二)
|
2天前
|
缓存 Java
《JVM由浅入深学习九】 2024-01-15》JVM由简入深学习提升分(生产项目内存飙升分析)
《JVM由浅入深学习九】 2024-01-15》JVM由简入深学习提升分(生产项目内存飙升分析)
9 0