Java虚拟机(JVM)
目录
- 内存区域划分
- 堆(Heap)
- 方法区(Method Area)
- 程序计数器(Program Counter Register)
- 虚拟机栈(VM Stack)
- 本地方法栈(Native Method Stack)
- 类加载的过程
- 类加载过程
- 类加载器
- 双亲委派模型
- 垃圾回收机制
- 1. 找出垃圾
- 2. 释放垃圾的内存空间
JVM(Java虚拟机)是Java Virtual Machine的缩写,是一种可以执行Java字节码的虚拟机。它为Java应用程序提供了一个与平台无关的执行环境,使得Java程序只需编写一次,就可以在安装了JVM的任何系统上运行
本文主要介绍三个部分:JVM内存区域划分、类加载的过程、垃圾回收机制
内存区域划分
运行时数据区划分如下
堆(Heap)
堆是JVM中最大的一块内存区域,主要用于存储对象实例,new出来的对象变量都是存储在栈上。堆是所有线程共享的内存区域(只有一份),并且是垃圾回收器的主要工作区域。堆内存大概分为以下几个部分:
- 新生代(Young Generation):大多数对象的生命周期都很短,因此年轻代被划分为三个部分:Eden区、Survivor0区(S0)、Survivor1区(S1)。对象通常在Eden区被创建,经过一次垃圾回收后,存活的对象会被移动到S0或S1区,每经过一次垃圾回收,存活的对象就会在S0和S1之间移动,直到晋升到老年代。
- 老年代(Old Generation):在年轻代中经历了多次垃圾回收仍然存活的对象,以及一些大对象(如大数组),会被移动到老年代。老年代的垃圾回收频率较低,因为这里存放的都是生命周期较长的对象。
方法区(Method Area)
方法区是所有线程共享的内存区域(只有一份),用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
程序计数器(Program Counter Register)
程序计数器是一块小的内存空间,它为每个线程私有。程序计数器保存了当前执行的字节码指令的地址(类似于咱们写代码时的行号)。在JVM中,字节码解释器通过改变程序计数器的值来控制执行流程。
程序计数器是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依靠这个计数器完成。
虚拟机栈(VM Stack)
虚拟机栈也是线程私有的内存区域,它的生命周期与线程相同。虚拟机栈用于存储局部变量表、操作栈、动态链接、方法出口等信息。每个方法在执行时都会创建一个栈帧(Stack Frame),用于存储局部变量和操作数栈,当方法执行结束时,栈帧会被销毁。
-
局部变量表(Local Variables):存储方法中的局部变量。
-
操作数栈(Operand Stack):用于存储计算过程中的中间数据。
-
动态链接(Dynamic Linking):将符号引用转换为直接引用。
-
方法出口(Method Exit):用于方法正常退出或异常退出时的清理工作。
本地方法栈(Native Method Stack)
本地方法栈与虚拟机栈类似,Java虚拟机栈是给JVM使用的,而本地方法栈是给本地方法使用的。
提问:下面变量(a、b、c、d、e、f)属于哪个内存区域?
class Test {private int a;private Test b = new Test();private static int c;private static Test d = new Test();public static void main(String[] args) {int e = 1;Test f = new Test();}
}
答:a、b属于堆,c、d属于方法区,e、f属于栈
变量属于哪个区和变量是否为内置类型无关
局部变量==>栈
成员变量==>堆
静态成员变量==>方法区
类加载的过程
JVM类加载是Java程序运行的基础,它涉及将类的.class文件从磁盘加载到内存中,并进行校验、链接和初始化等一系列步骤。
类加载过程
-
加载(Loading):
-
类加载过程的第一步,JVM通过类的全限定名获取其定义这个类的二进制字节流(例如,从.class文件、JAR文件或者网络中获取)。
-
将这个字节流所代表的静态存储结构转化为JVM内部运行时的数据结构。
-
在内存中创建一个代表该类的
java.lang.Class
对象,这个对象将作为程序访问这个类的接口。简单来说就是:根据类名找到对应的.class文件,打开文件并读取内容。
-
-
验证(Verification):
确保读取到的.class文件符合JVM规范,没有安全问题。包括文件格式验证、元数据验证、字节码验证和符号引用验证,确保类的信息是合法的,不会危害虚拟机的安全。 -
准备(Preparation):
为类对象分配内存空间,根据读取到的.class文件内容,确定类对象所需要的空间大小,申请出对应大小的空间,并设置默认初始值(通常是0)。 -
解析(Resolution):
将类、接口、字段和方法的符号引用转换为直接引用。符号引用就是类、字段、方法的全限定名,直接引用保存的是内存中的地址。 -
初始化(Initialization):
执行类的静态初始化和静态变量的赋值操作,执行类中的静态代码块。
类加载器
JVM内置了几种主要的类加载器:
-
启动类加载器(Bootstrap ClassLoader):
负责加载标准库中的类。
-
扩展类加载器(Extension ClassLoader):
负责加载扩展类。
-
应用程序类加载器(Application ClassLoader):
负责加载第三方库中的类、自己写的代码中的类。
双亲委派模型
双亲委派模型(Parent Delegation Model)是Java虚拟机(JVM)中的一种类加载机制。它的核心思想是,当一个类加载器收到类加载请求时,会先委托其父类加载器去完成这个请求,只有在父类加载器无法完成时,自己才会尝试去加载这个类。
双亲委派模型是类加载5个步骤中第一个步骤中的环节,
工作流程:通过得到的全限定名,
- 从Application ClassLoader开始进行加载,此时不会立刻扫描目录,而是把任务委派给Extension ClassLoader
- Extension ClassLoader拿到任务后,也不会立刻扫描目录,而是把任务委派给Bootstrap ClassLoader
- Bootstrap ClassLoader在目录中进行扫描加载对应的类,如果Bootstrap ClassLoader没找到对应的类,就把任务返回给Extension ClassLoader,如果找到了直接进行后续的类加载过程。类似的,如果Extension ClassLoader没找到,会继续吧任务返回给Application ClassLoader,如果Application ClassLoader找到了直接返回,如果Application ClassLoader没找到,就会抛出异常
类加载请求首先由父类加载器尝试完成,只有在父类加载器无法完成类加载时,才由子类加载器尝试自己去加载。
双亲委派模型机制就是为了防止用户自己定义的类和标准库的类名一样,把标准库的类覆盖掉。
垃圾回收机制
JVM的垃圾回收机制(Garbage Collection, GC)是Java内存管理的重要组成部分,主要目标是自动回收不再使用的对象,释放内存资源,避免内存泄漏和溢出。垃圾回收机制主要工作区域是堆。
GC是如何回收的?
1. 找出垃圾
方案1:给每个对象分配一个计数器,计数器记录的是当前有多少个变量引用了这个对象,每增加一个引用计数器就+1,每减少一个引用就-1。如果计数器为0,表示当前对象没有人引用它,说明这个对象就是垃圾。这个方案存在问题循环引用问题,并且会消耗额外的内存空间,JVM没有采用。
循环引用问题:
class Test {Test t = null;
}
//..........class Main {public static void main(String[] args) {Test a = new Test();Test b = new Test();a.t = b;b.t = a;a = null;b = null;}
}
//这两个对象引用计数器为0不能被释放,但是这两个对象又无法使用
方案2:可达性分析(JVM采用的方案)
JVM会从一组根对象(GCroot)开始,遍历所有可达对象(类似于二叉树、多叉树遍历)。如果某个对象不可通过根对象引用到达,JVM便会将其视为垃圾并回收。常见的GCroot:栈上的局部变量、方法区中的静态成员、常量池中引用指向的对象。
2. 释放垃圾的内存空间
有以下垃圾回收算法
标记-清除算法:
先标记出所有存活的对象,然后清除未被标记的对象。这种方法简单,但会产生大量内存碎片(不是连续的空间),后续申请内存的时候可能申请不了(申请的空间一定是连续的)。
复制算法:
将可用内存分为大小相等的两块,每次只使用一块,当一块用完,将存活的对象复制到另一块,然后清理已使用过的内存。这种方法解决了内存碎片问题,但代价是内存使用效率降低,复制成本比较大。
标记-整理算法:
类似于顺序表删除中间元素的操作。先标记出存活的对象,然后让所有存活的对象向一端移动,之后清理端边界以外的内存区域。这种方法解决了内存碎片问题。这种方法的代价是移动存活对象的开销比较大
分代收集算法(JVM采取的方案):
根据对象存活周期的不同,将堆分为新生代和老年代。
- 新生代(Young Generation):大多数对象的生命周期都很短,因此年轻代被划分为三个部分:Eden区、Survivor0区(S0)、Survivor1区(S1)。对象通常在Eden区被创建,经过一次垃圾回收后,存活的对象会被移动到S0或S1区,每经过一次垃圾回收,存活的对象就会在S0和S1之间移动,直到晋升到老年代。
- 老年代(Old Generation):在年轻代中经历了多次垃圾回收仍然存活的对象,以及一些大对象(如大数组),会被移动到老年代。老年代的垃圾回收频率较低,因为这里存放的都是生命周期较长的对象。
新生代中的对象生命周期短,适合使用复制算法;老年代中的对象生命周期长,适合使用标记-整理算法。
垃圾回收器
现在常见的垃圾回收器有CMS、G1
CMS(Concurrent Mark-Sweep)垃圾回收器:
减少STW(Stop The World),标记清除算法,并行处理大部分垃圾回收过程。可能产生内存碎片,老年代需要更大内存。
G1(Garbage First)垃圾回收器:
面向服务端的低延迟垃圾回收器,将堆划分为多个区域,优先回收垃圾最多的区域。并行和并发执行,适合大内存应用。
相关文章:

Java虚拟机(JVM)
目录 内存区域划分堆(Heap)方法区(Method Area)程序计数器(Program Counter Register)虚拟机栈(VM Stack)本地方法栈(Native Method Stack) 类加载的过程类加…...

MQ 架构设计原理与消息中间件详解(三)
RabbitMQ实战解决方案 RabbitMQ死信队列 死信队列产生的背景 RabbitMQ死信队列俗称,备胎队列;消息中间件因为某种原因拒收该消息后,可以转移到死信队列中存放,死信队列也可以有交换机和路由key等。 产生死信队列的原因 消息投…...

大数据新视界 --大数据大厂之 Alluxio 数据缓存系统在大数据中的应用与配置
💖💖💖亲爱的朋友们,热烈欢迎你们来到 青云交的博客!能与你们在此邂逅,我满心欢喜,深感无比荣幸。在这个瞬息万变的时代,我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…...
PHP基本语法总结
目录 输出语句 注释 数据类型(变量) 局部和全局作用域 类型比较(松散比较与严格比较) 常量 运算符 并置运算符 不等于 逻辑运算符 条件语句 数组 关联数组 数组排序 一般数组 关联数组 循环 函数 变量函数 魔…...
尚硅谷rabbitmq 2024第30-33节 死信队列 答疑
Virtual host: Type: Name: Durabiity: Arguments: Default for virtual host w ququt.normal.video Durable x-dead-letter-exchange x-dead-1etter-routing-xey x-mAx-1ength X-m在88点0也-6E1 exchange.dead.letter.vide zouting.key.dead.ietter.v 10 String String Number…...

解锁空间距离计算的多种方式-含前端、空间数据库、后端
目录 前言 一、空间数据库求解 1、PostGIS实现 二、GIS前端组件求解 1、Leaflet.js距离测算 2、Turf.js前端计算 三、后台距离计算生成 1、欧式距离 2、Haversice球面距离 3、GeoTools距离计算 4、Gdal距离生成 5、geodesy距离计算 四、成果与生成对比 1、Java不…...

Windows 开发工具使用技巧 QT使用安装和使用技巧 QT快捷键
一、QT配置 1. 安装 Qt 开发框架 1、下载 1、进入下载地址 下载地址1 (官方, 需注册账号): https://www.qt.io/download下载地址2(推荐): http://download.qt.io/http://download.qt.io/archive/qt/ (或更直接的…...

【实战教程】SpringBoot全面指南:快速上手到项目实战(SpringBoot)
文章目录 【实战教程】SpringBoot全面指南:快速上手到项目实战(SpringBoot)1. SpringBoot介绍1.1 SpringBoot简介1.2系统要求1.3 SpringBoot和SpringMVC区别1.4 SpringBoot和SpringCloud区别 2.快速入门3. Web开发3.1 静态资源访问3.2 渲染Web页面3.3 YML与Properti…...

LeetCode讲解篇之1043. 分隔数组以得到最大和
文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 对于这题我们这么考虑,我们选择以数字的第i个元素做为分隔子数组的右边界,我们需要计算当前分隔子数组的长度为多少时能让数组[0, i]进行分隔数组的和最大 我们用数组f表示[0, i)区间内的…...
Python知识点:结合Python工具,如何使用TfidfVectorizer进行文本特征提取
开篇,先说一个好消息,截止到2025年1月1日前,翻到文末找到我,赠送定制版的开题报告和任务书,先到先得!过期不候! 如何使用Python的TfidfVectorizer进行文本特征提取 在自然语言处理(…...

Diffusion models(扩散模型) 是怎么工作的
前言 给一个提示词, Midjourney, Stable Diffusion 和 DALL-E 可以生成很好看的图片,那么它们是怎么工作的呢?它们都用了 Diffusion models(扩散模型) 这项技术。 Diffusion models 正在成为生命科学等领域的一项尖端技术&…...

查找回收站里隐藏的文件
在Windows里,每个磁盘分区都有一个隐藏的回收站Recycle, 回收站里保存着用户删除的文件、图片、视频等数据,比如,C盘的回收站为C:\RECYCLE.BIN\,D盘的的回收站为D:\RECYCLE.BIN\,E盘的的回收站为E:\RECYCLE…...
[运维]2.elasticsearch-svc连接问题
Serverless 与容器决战在即?有了弹性伸缩就不一样了 - 阿里云云原生 - 博客园 当我部署好elasticsearch的服务后,由于个人习惯,一般服务会在name里带上svc,所以我elasticsearch服务的名字是elasticsearch-svc: [root…...

Ajax面试题:(第一天)
目录 1.说一下网络模型 2.在浏览器地址栏键入URL,按下回车之后会经历以下流程: 3.什么是三次握手和四次挥手? 4.http协议和https协议的区别 1.说一下网络模型 注:各层含义按自己理解即可 2.在浏览器地址栏键入URL,…...

数据仓库拉链表
数仓拉链表是数据仓库中常用的一种数据结构,用于记录维度表中某个属性的历史变化情况。在实际应用中,数仓拉链表可以帮助企业更好地进行数据分析和决策。 数仓拉链表(Slowly Changing Dimension, SCD)是一种用于处理维表中数据变化…...

【JVM】实战篇
1、内存调优 1.1 内存溢出和内存泄漏 内存泄漏(memory leak):在Java中如果不再使用一个对象,但是该对象依然在GC ROOT的引用链上,这个对象就不会被垃圾回收器回收,这种情况就称之为内存泄漏。 内存泄漏绝…...
2024年9月30日--10月6日(ue5肉鸽结束)
按照月计划,本周把ue肉鸽游戏完成,然后进行ue5太阳系 , 剩余14节,218分钟,如果按照10分钟的视频教程1小时进行完的话,则需要22小时,分布在10月2日-10月6日之间,每天44分钟的视频教程…...

【Python游戏开发】贪吃蛇游戏demo
准备步骤 项目开发使用【Mu 编辑器】 1.新建项目,并导入游戏图片 游戏编写 1.创建场景 SIZE 15 # 每个格子的大小 WIDTH SIZE * 30 # 游戏场景总宽度 HEIGHT SIZE * 30 # 游戏场景总高度def draw():screen…...
pytorch张量基础
引言张量的基础知识 张量的概念张量的属性张量的创建张量的操作 基本运算索引和切片形状变换自动微分 基本概念停止梯度传播张量的设备管理 检查和移动张量CUDA 张量高级操作 张量的视图广播机制分块和拼接张量的复制内存优化和管理 稀疏张量内存释放应用实例 线性回归神经网络…...

深入解析LlamaIndex Workflows【下篇】:实现ReAct模式AI智能体的新方法
之前我们介绍了来自LLM开发框架LlamaIndex的新特性:Workflows,一种事件驱动、用于构建复杂AI工作流应用的新方法(参考:[深入解析LlamaIndex Workflows:构建复杂RAG与智能体工作流的新利器【上篇】]。在本篇中ÿ…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...

RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...

Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...

初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...