JVM的组成--运行时数据区

ops/2025/3/21 23:22:58/

JVM的组成

1、类加载器(ClassLoader)

类加载器负责将字节码文件从文件系统中加载到JVM中,分为:加载、链接(验证、准备、解析)、和初始化三个阶段

2、运行时数据区

运行时数据区包括:程序计数器、虚拟机栈、堆、本地方法栈、方法区(元空间)

3、执行引擎

执行引擎负责将字节码(.class)解释或编译为机器码并执行。包括:解释器、即时编译器(JIT)、垃圾回收器

4、本地方法接口

本地方法接口提供JVM与本地方法库(如C/C++库)的交互能力,使得Java程序可以调用本地方法

一 程序计数器

程序计数器 :是线程私有的,内部保存字节码(.class文件)的行号。用于记录正在执行的字节码指令的地址

由于是线程私有的,所以不会有线程安全问题

使用javap -v xx.class 可以打印堆栈大小,局部变量的数量和方法的参数

可以看到如下图:

class的每一行都被解析成更多的执行命令(指令),每一行前面都有一个行号(地址),记录程序已经执行到哪一行

有什么用呢?比如:当线程1 执行到第10行,这个时候如果CPU被线程2抢走了

等到下次执行的时候,线程1不必从头执行,直接从第10行继续执行

这就是程序计数器的作用

二 堆

堆是线程共享的区域,主要用来保存对象实例,数组等大的、或者生命周期长的对象,当那个堆中没有内存空间可分配给实例,也无法再扩展时,则抛出OutOfMemoryError异常

既然是线程共享的区域,就说明可能有线程安全的问题

如图所示,堆分为两块区域

1、年轻代:分为Eden区,两个大小严格相同的Survivor区S0和S1

根据GC的策略,在经过几次垃圾回收后,仍然存活于Survivor区的对象将被移动到老年代

2、老年代:主要保存一些声明周期长的对象,一般是一些老的对象

三 虚拟机栈

Java Virtual machine Stacks(Java虚拟机栈):

1、是每个线程运行时所需的内存,称为虚拟机栈,先进后出,一般会用来存储局部变量、方法调用

保存局部变量表、操作数栈、动态链接和方法出口等信息

2、如果有多个线程,则会创建多个虚拟机栈,因此只要是在虚拟机栈内,则是线程安全的

每个栈由多个栈帧(frame)组成,对应着每次方法调用时所占用的内存,一个方法一个栈帧

当方法调用方法时,就会在栈中,出现一条栈帧链

3、每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

如图:方法1调用方法2,方法2调用方法3

则方法1的栈帧,就会先进入栈中,然后调用方法2,方法2的栈帧进入栈中,其次是方法3的栈帧3

最后:当方法3执行完毕,则方法3的栈帧先弹出(弹栈),然后是方法2的栈帧2,最后是栈帧1

3.1 垃圾回收是否涉及栈内存?

不涉及,垃圾回收指的是 回收 堆内存,当栈帧弹栈以后,内存就会释放

3.2 栈内存分配越大越好吗?

未必,默认的栈内存通常为1024K,也就是1MB

栈帧过大会导致线程数变少,例如,机器总内存为512MB,目前能活动的线程数就为512个(每个线程1MB),但是如果把栈内存改为2048K,也就是2MB,那么能够活动的栈帧就会减半

3.3 方法内的局部变量是否线程安全?

上面说到,在同一个虚拟机栈内,是线程安全的,但是就一定能说明方法内的局部变量是线程安全的吗?

不一定

如果方法内的局部变量没有逃离方法的作用范围,则这个局部变量是线程安全的

如果是局部变量引用了对象,并逃离方法的作用范围,则需要考虑线程安全的问题

3.4 栈内存会溢出吗

栈内存是线程私有的,会有内存溢出的问题吗?

有的兄弟,有的

1、栈帧过多导致的内存溢出,典型问题:递归调用

如下的无限递归调用,会导致栈帧有无限多个,导致栈内存溢出

2、栈帧过大导致栈内存溢出

这个不太容易出现,因为一个栈帧一般最大可以有1MB的内存大小,一般不会溢出

3.5 堆和栈的区别是什么

1、栈内存一般会用来存储局部变量、方法调用;但是堆内存是用来存储Java对象和数组的。

堆会GC垃圾回收,而栈不会

2、栈内存是线程私有的,而堆内存是线程共有的

3、两者内存溢出的报错不同:

栈空间不足:java.lang.StackOverFlowError

堆内存不足:java.lang.OutOfMemoreError

四 方法区(元空间)

1、方法区(Method Area)是各个线程共享的内存区间,用的是本地内存,也就是操作系统的内存

注意,是操作系统中的内存

2、主要存储类的信息、运行时常量池等

3、虚拟机启动的时候创建,关闭虚拟机时释放

4、如果方法区的内存无法满足分配请求,则会抛出OutOfMemoryError:MetaSpace

元空间是没有上限的,除非达到虚拟机内存的大小,或者你手动设值一个元空间的最大值

比如你通过如下命令:-XX:MaxMetaspaceSize=8m

修改元空间最大值为8MB,然后你开一个循环,使用ClassWriter加载10000个类,就可能把元空间撑爆:

MaxMetaspaceSize is too small

4.1 直接内存是什么?

直接内存不属于JVM的内存结构,不由JVM进行管理。是虚拟机的系统内存

常见于NIO操作时,用于数据缓冲区,分配回收成本较高,但读写能力高,不受JVM内存回收管理

因为Java代码是无法直接操作内存的,只能通过建立一个java缓冲区byte[],拷贝系统缓存区的数据,所以会比直接NIO操作直接内存来的慢

五 本地方法栈

本地方法栈(Native Method Stack)是JVM中为执行本地方法(Native Method)而准备的一块内存区域。本地方法指的是使用Java以外的语言(例如C或C++)编写,并通过JNI(Java Native Interface)或其他方式被Java程序调用的方法。

如果你在jdk中看到native修饰的方法,那么就是本地方法


http://www.ppmy.cn/ops/167663.html

相关文章

word报告篇:python生成《蔬菜店销售数据分析报告》案例

原创 IT小本本 IT小本本 2025年03月20日 00:39 北京 通过源码篇《源码篇:python生成《蔬菜店销售数据分析报告》案例》运行结果后,我们可以得到下面的word报告! 1. 介绍 本报告旨在展示使用Python进行蔬菜店销售数据分析的过程和结果。…

Github 2025-03-20 Go开源项目日报Top10

根据Github Trendings的统计,今日(2025-03-20统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Go项目10GitHub CLI(gh):GitHub官方命令行工具 创建周期:1813 天开发语言:Go协议类型:MIT LicenseStar数量:36710 个Fork数量:5625 次关…

手撸一个 deepseek 聊天历史对话、多轮对话(ollama + deepseek + langchain + flask)

前言 为了让聊天更加智能化,体验更好,打算搞个聊天历史对话,让 deepseek 可以结合上下文回答。 一、准备环境 ollama,用于在本地运行、部署和管理大型语言模型(LLMs)。deepseek 模型,本文用的…

HTTP核心知识

理解 HTTP 协议是优化 Web 应用性能、调试问题和实现高效通信的基础。以下是前端开发者需要掌握的 核心 HTTP 知识: 1. HTTP 基础概念 请求与响应模型 理解客户端(浏览器)发送 HTTP 请求,服务器返回 HTTP 响应的基本流程。 HTTP …

Powershell WSL导出导入ubuntu22.04.5子系统

导出Linux子系统 导出位置在C盘下,根据自己的实际情况更改即可Write-Host "export ubuntu22.04.5" -ForegroundColor Green wsl --export Ubuntu-22.04 c:\Ubuntu-22.04.tar 导入Linux子系统 好处是目录可用在任意磁盘路径,便于迁移不同的设备之间Write-Host &quo…

单元测试、注解

目录 一、单元测试1.快速入门2.Junit在实际开发中的用法 二、注解1.注解概述2.自定义注解3.元注解4.解析注解 一、单元测试 单元测试就是针对最小的功能单元编写测试代码,Java程序最小的功能单元是方法。因此,单元测试就是针对Java方法的测试&#xff0…

31天Python入门——第5天:循环那些事儿

你好,我是安然无虞。 文章目录 1. while循环1.1 while循环的嵌套1.2 补充学习:print函数 2. for循环2.1 range函数2.2 for循环2.3 continue和break以及return2.4 for循环的嵌套 3. 补充学习3.1 enumerate函数3.2 zip函数3.3 不要在遍历列表的过程中删除元素 循环 是…

压缩Docker虚拟磁盘空间CMD命令

以管理员身份运行PowerShell ‌关闭所有WSL/Docker服务‌ wsl --shutdown ‌执行清理命令 Optimize-VHD -Path "D:\wsl\DockerDesktopWSL\data\ext4.vhdx" -Mode Full