【 java 虚拟机知识 第一篇 】
目录
1.内存模型
1.1.JVM内存模型的介绍
1.2.堆和栈的区别
1.3.栈的存储细节
1.4.堆的部分
1.5.程序计数器的作用
1.6.方法区的内容
1.7.字符串池
1.8.引用类型
1.9.内存泄漏与内存溢出
1.10.会出现内存溢出的结构
1.内存模型
1.1.JVM内存模型的介绍
内存模型主要分为五个部分:虚拟机栈,本地方法栈,堆,方法区(永久代或元空间),程序计数器,当然还有一部分是直接内存。
虚拟机栈:每个线程各有一个,线程独有,当执行方法(除了本地方法native修饰的方法)之前,会创建一个栈帧,栈帧里面包含局部变量表和操作数栈和动态链接和方法出口等信息,而每个栈帧就是存入栈中
本地方法栈:每个线程各有一个,线程独有,当执行本地方法时类似于虚拟机栈,一样会创建栈帧,存入对应信息
程序计数器:每个线程各有一个,线程独有,它的作用是记录当前线程下一次执行的二进制字节码指令地址,如果执行的是本地方法那么它会记录为定值(null)
堆:所有线程共享,堆的回收由垃圾回收机制管理,堆中主要存入对象实例信息,类信息,数组信息,堆是JVM内存中最大的一个
永久代:在jdk1.7及以前是方法区的实现,使用的是jvm内存,独立于堆,主要存入类信息,静态变量信息,符号引用等信息
元空间:在jdk1.8及以后是方法区的实现,使用的是本地内存,主要存入类信息,静态变量信息,符号引用等信息
直接内存:该内存属于操作系统,由NIO引入,操作系统和Java程序都可以进行操作,实现共享
----
常量池:属于class文件的一部分,主要存储字面量,符号引用
----
运行时常量池:属于方法区,其实就是将常量池中的符号引用替换成了直接引用,其余一样
1.2.堆和栈的区别
五个点:用途,生命周期,存储速度,存储空间,可见性
用途:栈主要存储方法返回地址,方法参数,临时变量,每次方法执行之前会创建栈帧,而堆存储对象的实例信息,类实例信息,数组信息
生命周期:栈的生命周期可见,每次方法执行完栈帧就会移除(弹出),而堆中的数据需要由垃圾回收器回收,回收时间不确定
存储速度:栈的速度更快,栈保持"先进后出"的原则,操作简单快,而堆需要对对象进行内存分配和垃圾回收,并且垃圾回收器本身运行也会损耗性能,速度慢
存储空间:栈的空间相对于堆的空间小,栈的空间小且固定,由操作系统管理,而堆的空间是jvm中最大的,由jvm管理
可见性:栈是每个线程都有的,而堆是所有线程共享的
1.3.栈的存储细节
如果执行方法时,里面创建了基本类型,那么基本类型的数据会存入栈中,如果创建了引用类型,会将地址存入栈,其实例数据存入堆中
1.4.堆的部分
堆主要分为两部分:新生代,老年代,它的比例:1:2
新生代:新生代分为两个区:伊甸园区和幸存者区,而幸存者区又平均分为S0和S1区,伊甸园区与S0与S1之间的比例:8:1:1,每次新创建的对象实例都会先存入伊甸园区,它们主要使用的垃圾回收算法是复制算法,当伊甸园区的内存使用完时,会使用可达性分析算法,标记不可存活的对象(没有被引用的对象)将存活对象复制移入S0或S1中,这个过程叫Minor GC,如果这次移入的是S0,那么下次就会将伊甸园区和S0中的对象移入S1中,循环反复,每经历一次Minor GC过程就会给对象年龄加一,直到大于等于15时,会认为该对象生命周期长,移入老年代中
细节:其实新创建的对象不会直接存入伊甸园区,如果多线程情况下同时进行存入对象(线程竞争压力大)会导致性能的损失,因此会给每个线程从伊甸园区中先申请一块TLAB区域,先将对象存入该区,如果该区内存使用完,会重写申请或直接存入伊甸园区
老年代:老年代就是存储生命周期长的对象(不经常回收的对象),主要使用的垃圾回收算法为标记清除算法或标记整理算法,看场景出发,其中老年代还包含一个大对象区
大对象区:主要存储的就是新创建的大对象比如说大数组,会直接将该对象存入大对象区中,不在存入新生代
可达性分析算法:从GC Root出发找对应引用对象,如果一个对象没有被直接引用或间接引用,那么会被标记,GC Root可以是java的核心库中的类,本地方法使用的类,还未结束的线程使用的类,使用了锁的类
标记清除算法:对引用的对象进行标记,然后进行清除(不是真正的清除,而是记录其对象的起始地址和结束地址到一个地址表中,下次要添加新对象时会先从表中找,找到一个适合大小的就会进行覆盖),清除:记录地址,新对象进行覆盖,好处:速度快,缺点:内存碎片化严重(内存不连续了,本来可以存入的对象存入不了)
标记整理算法:同理进行标记,然后再对可存活对象进行整理,最后清除,好处:避免了内存碎片化问题,缺点:速度慢
复制算法:将内存空间分为两份,一份存对象from,一份为空to,当要回收时,复制可存活对象移入为空的内存空间to中(移入既整理),然后对存对象的空间from整体清除,然后名称from和to换过来
为什么会有大对象区:因为伊甸园区的内存空间本身就不大,如果你直接创建一个大于它空间的对象,会出现问题,还有就是即使没有超过伊甸园区的空间,但是其对象依旧很大,频繁的复制移动很影响性能
1.5.程序计数器的作用
简单来说:线程1执行到某个地方时,线程2抢到了执行权,那么等到线程1执行时是不是需要知道上次执行到哪里了,所以程序计数器就是记录执行到哪里的,并且每次线程都需要有一个来记录
1.6.方法区的内容
方法区主要包含:类信息,静态变量信息,运行时常量池,即时编译器的缓存数据
1.7.字符串池
在jdk1.6及以前字符串池属于永久代,jdk1.7字符串池移入堆中但是还是属于永久代的,jdk1.8及以后还是存入堆中,但是不属于元空间了(1.7以前是永久代,1.8以后是元空间)
细节:String s1 = "a";它的过程是:先去字符串池中找,看是否能找到该字符,找到了直接复用池中地址,没有找到会先在堆中创建一个String对象,jdk1.6它会将数据复制一份重新创建一个新的对象存入池中,jdk1.7会将其地址复用给池中
String s2 = new("b");同理
String s3 = "a" + "b";常量进行相加,与new("ab")基本一致
String s4 = s1 + s2;变量相加,底层使用的是new StringBuilder.append("a").append("b").toString(),如果池中存在"ab",它也不会复用,而是直接创建,如果池中不存在,而不会将新创建的对象存入池中
1.8.引用类型
引用类型:强引用,软引用,弱引用,虚引用,(终结器引用)
强引用:比如new就是,只要有强引用指向对象,那么该对象永远不会被回收
软引用:如果出现内存溢出的情况,再下次GC时会对其回收
弱引用:每次进行GC过程都会进行回收
虚引用:每次进行GC过程都会进行回收
细节:这些都是对象,等级依次递减
软引用:创建一个软引用对象时你可以指定引用队列,如果不指定会导致软引用为null一个空壳,比如说出现了GC Root强引用软引用对象,导致软引用对象无法被回收,你想要其对象被回收,可以使用引用队列,简单来说就是出现了这种情况,将软引用对象存入队列中,下次GC会扫描队列进行回收,当然这是特殊情况,总结来说:软引用可以使用引用队列也可以不使用
public class SoftRefDemo {public static void main(String[] args) throws InterruptedException {// 1. 创建引用队列ReferenceQueue<Object> queue = new ReferenceQueue<>();// 2. 创建大对象(确保能被GC回收)byte[] data = new byte[10 * 1024 * 1024]; // 10MB// 3. 创建软引用并关联队列SoftReference<Object> softRef = new SoftReference<>(data, queue);// 4. 移除强引用(只保留软引用)data = null;System.out.println("GC前: ");System.out.println(" softRef.get() = " + softRef.get());System.out.println(" queue.poll() = " + queue.poll());// 5. 强制GC(模拟内存不足)System.gc();Thread.sleep(1000); // 给GC时间System.out.println("\nGC后: ");System.out.println(" softRef.get() = " + softRef.get());System.out.println(" queue.poll() = " + queue.poll());}
}
GC前: softRef.get() = [B@15db9742queue.poll() = nullGC后: softRef.get() = nullqueue.poll() = java.lang.ref.SoftReference@6d06d69c
弱引用:与软引用相同
WeakHashMap<Key, Value> map = new WeakHashMap<>();Key key = new Key();
map.put(key, new Value());// 移除强引用
key = null;System.gc();// GC后Entry自动被移除
System.out.println(map.size()); // 输出: 0
虚引用:最好的例子就是直接内存:它就是使用了虚引用,直接内存就是从操作系统中申请了一块空间来使用,因此GC是不能对其进行回收的,如果当强引用消失只剩下虚引用,那么会将虚引用对象存入引用队列中,等队列来执行本地方法释放直接内存
public class PhantomRefDemo {public static void main(String[] args) {// 1. 创建引用队列ReferenceQueue<Object> queue = new ReferenceQueue<>();// 2. 创建虚引用Object obj = new Object();PhantomReference<Object> phantomRef = new PhantomReference<>(obj, queue);// 3. 模拟直接内存分配ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024 * 1024); // 1MBSystem.out.println("GC前:");System.out.println(" phantomRef.get() = " + phantomRef.get()); // nullSystem.out.println(" queue.poll() = " + queue.poll()); // null// 4. 移除强引用(触发回收条件)obj = null;directBuffer = null; // 释放DirectByteBuffer强引用// 5. 强制GC(实际应用中会自动触发)System.gc();try { Thread.sleep(500); } catch (Exception e) {}System.out.println("\nGC后:");System.out.println(" phantomRef.get() = " + phantomRef.get()); // nullSystem.out.println(" queue.poll() = " + queue.poll()); // 返回phantomRef对象// 6. 实际效果:DirectByteBuffer分配的1MB堆外内存已被释放}
}
终结器引用:在所有父类Object中有一个终结器方法finalize()方法,如果重写该方法,那么执行GC之前会先执行该方法,当没强引用指向了,而这个对象还重写了finalize()方法,那么会将这个终结器引用对象加入队列中,下次GC时会先由队列来执行finalize()方法,但是指定执行的队列是一个优先级不高的队列,会导致资源释放缓慢
public class ResourceHolder {// 重写finalize方法(不推荐!)@Overrideprotected void finalize() throws Throwable {releaseResources(); // 释放资源super.finalize();}
}
1.9.内存泄漏与内存溢出
内存泄漏:就是说没有被引用的对象没有被回收,导致可用内存空间减少
比如:
- 静态集合没有释放:一直存在
- 线程未释放:线程应该执行完了,但是没有释放
- 事件监听:事件源都不存在了,还在监听
例子:使用对应的文件流,字节流,但是没有释放该流,就会导致内存泄漏
解决:释放流
内存溢出:就是说内存不足了
比如:
- 一直创建新对象
- 持久引用:集合一直添加但是没有被清除
- 递归
例子:ThreadLocal,每个线程都有一个ThreadLocal,本质就是每个线程存在一个ThreadLocalMap对象,key(弱引用)存入的是TreadLocal的实例,value(强引用)为自己指定的Object对象,如果没有使用该TreadLocal了,也就是说没有强引用指向TreadLocalMap对象,那么其中的key就会被设置为null,那如果该线程一直不结束,导致key不能被回收,随着key为null的情况增多就会导致内存溢出
解决:使用TreadLocal.recome();
1.10.会出现内存溢出的结构
会出现该问题的内存结构:堆,栈,元空间,直接空间
相关文章:

【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...

(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...

day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...

群晖NAS如何在虚拟机创建飞牛NAS
套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...

android13 app的触摸问题定位分析流程
一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...
MinIO Docker 部署:仅开放一个端口
MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...
怎么让Comfyui导出的图像不包含工作流信息,
为了数据安全,让Comfyui导出的图像不包含工作流信息,导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo(推荐) 在 save_images 方法中,删除或注释掉所有与 metadata …...
作为测试我们应该关注redis哪些方面
1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...

基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...
解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist
现象: android studio报错: [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决: 不要动CMakeLists.…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...

基于Java+VUE+MariaDB实现(Web)仿小米商城
仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意:运行前…...
PostgreSQL——环境搭建
一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在࿰…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

Qemu arm操作系统开发环境
使用qemu虚拟arm硬件比较合适。 步骤如下: 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载,下载地址:https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...

STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...

Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...

PHP 8.5 即将发布:管道操作符、强力调试
前不久,PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5!作为 PHP 语言的又一次重要迭代,PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是,借助强大的本地开发环境 ServBay&am…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...

Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)
引言 工欲善其事,必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后,我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集,就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...