[java基础-JVM篇]1_JVM自动内存管理

news2025/2/27 16:24:59
JVM内存管理涉及但不限于类加载、对象分配、垃圾回收等,本篇主要记录运行时数据区域与对象相关内容。
内容主要来源《深入理解Java虚拟机:JVM高级特性与最佳实践》与官方文档,理解与表述错漏之处恳请各位大佬指正。

目录

运行时数据区域

栈帧

栈异常

栈内存大小配置

配置参数

配置参数

字符串常量池(注意,字符串常量池自jdk8起在堆)

静态变量特别说明

方法区

运行时常量池

常量说明

常量分析实例

配置参数

程序计数器

直接内存

总结

对象

对象创建

内存分配(更新操作)的原子性原理

对象访问

内存泄漏分析(生产)

大对象

大对象的影响

解决


运行时数据区域

内存管理基于:内存动态分配与垃圾收集技术。
编译期间做的事情:给变量分配空间

一般讲的都是虚拟机栈,更多情况下指虚拟机栈中局部变量表部分。本地方法栈执行的都是本地的Native方法。

栈帧

每个线程都有自己的虚拟机栈,而方法作为执行的基本单元,在 每个方法执行的时候都会创建一个栈帧Stack Frame用于存储局部变量表、操作数栈、动态连接、方法出口信息等。
其中 局部变量表存放了编译期可知的 基本数据类型、对象引用
(了解就行)栈帧中存储的具体信息包括:
  1. 方法参数:包括传递给方法的实际参数以及方法的隐式参数,如 this 指针。
  2. 局部变量:定义在方法中的变量,包括基本类型和对象引用。
  3. 操作数栈:用于存储方法执行过程中产生的临时数值,如算术运算的结果、方法调用的返回值等。
  4. 方法返回地址:指向方法调用者应该返回的下一条指令的地址。
  5. 异常处理器:存储方法对应的异常处理器的信息,用于处理方法执行中可能发生的异常。
  6. 其它:包括方法的访问标志、常量池引用等。

栈异常

StackOverflowError
栈溢出
OutOfMemoryError
对于栈的提示是 java.lang.OutOfMemoryError: unable to create new native thread。此时无法创建新线程

栈内存大小配置

HotSpot虚拟机的栈容量不可动态拓展,在配置JVM时通过-参数Xss=256k配置。
JVM的总内存大小会影响线程栈内存的可用量。如果堆内存或者其他内存区域占用过多,剩余内存可能不足以支持大量线程。

配置参数

-Xss栈大小设置,在64位Linux系统上,栈大小默认1M

是垃圾回收管理器管理的区域,储物理上可以不连续,逻辑上是连续的内存空间。因垃圾收集器基于分代收集理论设计,所以将 分代划分(方便回收和分配内存)。
“新生代、老年代、Eden空间、From Survivor空间、To Survivor空间”。
堆:1:2=新生代:老年代, 其中新生代8:1:1 eden:from:to
存放几乎所有对象实例与数组(基本类型数组与对象类型数组)、字符串常量、静态变量。

配置参数

-Xms堆内存起始大小 -Xmx最大堆内存

字符串常量池(注意,字符串常量池自jdk8起在堆)

专门用于存储字符串字面量的一个特殊区域。它的设计目的是通过缓存机制避免重复创建相同的字符串对象,从而节省内存并提高性能。
值得注意的是,HotSpotJVM自JDK8将字符串常量池实现为堆中的一部分,从方法区移出,以优化内存管理并减少方法区的压力。
资料来源:
JEP 122: Remove the Permanent Generation
Chapter 5. Loading, Linking, and Initializing

静态变量特别说明

有资料说在方法区、有资料说在堆,给我整不会了。直接看官网
JEP 122: Remove the Permanent Generation
结论:静态变量引用或基本类型值静态变量在元空间,对象实例(包括静态对象的值)在堆中

方法区

方法区也是线程共享的内存区域,主要用于存储已被虚拟机加载的 类型信息、静态变量对象的引用与基本类型静态变量、即时编译器编译后的代码缓存等数据。是基于本地内存实现的,方法区内存回收主要目标是常量池的回收和对类型的卸载

运行时常量池

运行时常量池是方法区的一部分,是常量池表(Class文件常量池)运行时的表示形式,其中常量池表存储的是 符号引用(Symbolic References)和 字面量(Literal Values),而 不是实际的数据存储位置。它的核心作用是:
  • 存储类、方法、字段的全限定名描述
  • 保存方法描述符(Descriptor)
  • 存储字符串常量字面量
  • 记录数值型常量的原始值

常量说明

在代码中,使用final声明的常量会放到方法区运行时常量池
方法内变量=临时变量,存储在虚拟机栈中

常量分析实例

配置参数

-XX:MetaspaceSize:初始触发 GC 的阈值,默认约 21MB。
-XX:MaxMetaspaceSize:最大大小,默认无上限(受本地内存限制)。
-XX:MinMetaspaceFreeRatio 和 -XX:MaxMetaspaceFreeRatio:控制 GC 后空闲比例,默认 40% 和 70%。

程序计数器

作为游标指示字节码命令执行,每个线程的程序计数器是独立的。
计数器的值是正在执行的虚拟机字节码指令的地址。

直接内存

直接内存是指Java应用程序在JVM堆之外直接向操作系统申请并管理的一段内存区域。堆外内存的一种具体实现形式。
使用Native函数分配的堆外内存,用与NIO等操作。
设置虚拟机内存时,需要小于物理内存(给直接内存留空间)。因为运行时JVM内存=设置的内存+直接内存,实际占用的比设置的要大。防止动态扩展时出现OutOfMemoryError异常。
堆外内存通过DirectByteBuffer进行操作,避免在Java堆中和Native堆中来回复制数据。
需要注意的就是物理内存应该>jvm内存

总结

对象

对象可以划分为:对象头(Header)、实例数据(Instance)、对齐填充(Padding).
对象头包括两类信息。一部分包括对象自行运行时数据:哈 希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部 分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32个比特和64个比特,官方称它 为“Mark Word”。
另一部分是类型指针,用于JVM确认对象所属类实例。

对象创建

JVM执行new指令时,会区常量池定位这个类的符号引用,检查这个符号引用代表的类是否已经被加载、解析、初始化过,没有则进行类加载。
为新对象分配内存的方式大致分为两种:
  • 堆为对象分配内存的方式
    • “指针碰撞”——假设Java堆中内存是绝对规整的,所有被使用过的内存都被放在一 边,空闲的内存被放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把那 个指针向空闲空间方向挪动一段与对象大小相等的距离。
    • 空闲列表——假设Java堆中的内存并不是规整的,已被使用的内存和空闲的内存相互交错在一起,那 就没有办法简单地进行指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存块是可用的,在分 配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录。
JVM选择哪种依据其垃圾收集器决定。
内存分配(更新操作)的原子性原理
划分空间时,在并发情况下可能是不安全的。例如对象A与对象B同时使用指针分配内存。
JVM默认采用CAS(Compare And Swap)搭配失败重试的方式保证操作的原子性。

对象访问

HotSpot主要使用直接指针访问对象。

内存泄漏分析(生产)

JVM配置参数-XX:+HeapDumpOnOutOfMemoryError 可以让虚拟机在出现内存溢出异常的时候Dump当前内存堆转储快照。
也可以使用JMap命令导出Dump。
然后使用mat工具进行分析,如下:

大对象

占用较大连续内存空间的对象,一般是大数组、包含大量字段或嵌套对象的对象(复杂对象结构)、大型字符串对象。
将大对象直接分配到老年代可以避免“占用新生代内存导致提前GC带来的性能损耗”、“大对象在垃圾回收时的复制带来的性能损耗”,是一种JVM优化策略。
值得注意的是,JDK8默认收集器是Parallel Scavenge与Parallel Old,大对象不会直接放到老年代。会照常进行回收流程。
JVM在配置使用Serial和parNew两款收集器时,可以通过参数-XX:PretenureSizeThreshold来设定一个阈值,如果对象的大小超过阈值会被视为“大对象”,并被直接分配到老年代。
对于G1收集器,其引入了“HUmongous对象”的概念,任何超过Region大小一半的对象都会被视为巨型对象直接分配到老年代。

大对象的影响

  • 内存分配:大对象可能需要分配较大的连续内存块,这可能导致堆内存碎片化问题。
  • 垃圾回收:某些垃圾回收器(如 G1 GC)对大对象有特殊处理机制,可能会直接分配到老年代(而不是新生代)。

解决

在日常编码中要尽量避免大对象,不可避免时尽量复用,如使用对象池。
对于大数组对象,进行分割、将其分割为小对象进行操作。
或者使用手动释放的堆外内存。
 

try {

        ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024);

        // 使用 buffer

} finally {

        if (buffer != null && buffer.isDirect()) {

                ((sun.misc.Cleaner) sun.misc.Unsafe.getUnsafe().invokeCleaner(buffer)).clean();

        }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2306964.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】vim 设置

【Linux】vim 设置 零、起因 刚学Linux,有时候会重装Linux系统,然后默认的vi不太好用,需要进行一些设置,本文简述如何配置一个好用的vim。 壹、软件安装 sudo apt-get install vim贰、配置路径 对所有用户生效: …

基于Python+django+mysql旅游数据爬虫采集可视化分析推荐系统

2024旅游推荐系统爬虫可视化(协同过滤算法) 基于Pythondjangomysql旅游数据爬虫采集可视化分析推荐系统 有文档说明 部署文档 视频讲解 ✅️基于用户的协同过滤推荐算法 卖价就是标价~ 项目技术栈 Python语言、Django框架、MySQL数据库、requests网络爬虫…

Oracle 12c Docker安装问题排查 sga_target 1536M is too small

一、问题描述 在虚拟机环境(4核16GB内存)上部署 truevoly/oracle-12c 容器镜像时,一切运行正常。然而,当在一台 128 核 CPU 和 512GB 内存的物理服务器上运行时,容器启动时出现了 ORA-00821 等错误,提示 S…

es-head(es库-谷歌浏览器插件)

1.下载es-head插件压缩包,并解压缩 2.谷歌浏览器添加插件 3.使用

Linux网络基础(协议 TCP/IP 网络传输基本流程 IP VS Mac Socket编程UDP)

文章目录 一.前言二.协议协议分层分层的好处 OSI七层模型TCP/IP五层(或四层)模型为什么要有TCP/IP协议TCP/IP协议与操作系统的关系(宏观上是如何实现的)什么是协议 三.网络传输基本流程局域网(以太网为例)通信原理MAC地址令牌环网 封装与解包分用 四.IP地址IP VS Mac地址 五.So…

Web开发:ORM框架之使用Freesql的导航属性

一、什么时候用导航属性 看数据库表的对应关系,一对多的时候用比较好,不用多写一个联表实体,而且查询高效 二、为实体配置导航属性 1.给关系是一的父表实体加上: [FreeSql.DataAnnotations.Navigate(nameof(子表.子表关联字段))]…

NLP07-朴素贝叶斯问句分类之数据集加载(1/3)

一、概述 数据集加载(Dataset Loading)是机器学习、自然语言处理(NLP)等领域中的一个重要步骤,指的是将外部数据(如文件、数据库、网络接口等)加载到程序中,以便进行后续处理、分析…

Rk3568驱动开发_点亮led灯(手动挡)_5

1.MMU简介 完成虚拟空间到物理空间的映射 内存保护设立存储器的访问权限,设置虚拟存储空间的缓冲特性 stm32点灯可以直接操作寄存器,但是linux点灯不能直接访问寄存器,linux会使能mmu linux中操作的都是虚拟地址,要想访问物理地…

LangChain构建行业知识库实践:从架构设计到生产部署全指南

文章目录 引言:行业知识库的进化挑战一、系统架构设计1.1 核心组件拓扑1.2 模块化设计原则二、关键技术实现2.1 文档预处理流水线2.2 混合检索增强三、领域适配优化3.1 医学知识图谱融合3.2 检索结果重排序算法四、生产环境部署4.1 性能优化方案4.2 安全防护体系五、评估与调优…

Vscode编辑器:解读文件结构、插件的导入导出、常用快捷键配置技巧及其常见问题的解决方案

一、文件与文件夹结构 1.文件结构 文件名作用.babelrc配置 Babel 编译选项,指定代码转译规则。.editorconfig定义项目代码格式规范,如缩进风格和空格数量等。.eslintignore列出 ESLint 忽略的文件或文件夹。.eslintrc.js配置 ESLint 的规则和插件。.gi…

线性回归 (Linear Regression)案例分析1

广告费用与产品销量 工欲善其事必先利其器数据分析1. 检查缺失值、异常值3. 散点图查看特征、响应相关性3. 热力图查看特征、响应相关性 特征工程1、导入必要工具包2、读取数据3、数据标准化4、保存特征工程的结果到文件,供机器学习模型使用 模型选择读取数据数据准…

uni-app集成sqlite

Sqlite SQLite 是一种轻量级的关系型数据库管理系统(RDBMS),广泛应用于各种应用程序中,特别是那些需要嵌入式数据库解决方案的场景。它不需要单独的服务器进程或系统配置,所有数据都存储在一个单一的普通磁盘文件中&am…

【HTML— 快速入门】HTML 基础

准备工作 vscode下载 百度网盘 Subline Text 下载 Sublime Text下载 百度网盘 vscode 下载 Sublime Text 是一款轻量好用的文本编辑器,我们在写前端代码时,使用 Sublime Text 打开比使用记事本打开,得到的代码体验更好,比 vscode…

【MATLAB中的图像数据结构】

MATLAB中的图像数据结构 目录 MATLAB中的图像数据结构目标 :知识点 :1. 图像的存储方式 :2. 图像的颜色空间 :3. 图像的像素操作 : 示例代码 :1. 读取和显示图像 :2. 查看图像信息 :…

在线抽奖系统——项目介绍

目录 项目介绍 页面预览 需求分析 管理员登录注册 人员模块 奖品模块 活动模块 抽奖模块 系统设计 系统架构 项目环境 数据库设计 安全设计 完整代码:项目完整代码/在线抽奖系统/lottery-system Echo/project - 码云 - 开源中国 项目介绍 利用 MySQ…

JavaScript 系列之:Ajax、Promise、Axios

前言 同步:会阻塞。同步代码按照编写的顺序逐行依次执行,只有当前的任务完成后,才会执行下一个任务。 异步:异步代码不会阻塞后续代码的执行。当遇到异步操作时,JavaScript 会将该操作放入任务队列中,继续…

鸿蒙开发深入浅出01(基本环境搭建、页面模板与TabBar)

鸿蒙开发深入浅出01(基本环境搭建、页面模板与TabBar) 1、效果展示2、下载 DevEco Studio3、创建项目4、新建页面模板5、更改应用信息6、新建以下页面7、Index.ets8、真机运行9、图片资源文件 1、效果展示 2、下载 DevEco Studio 访问官网根据自己的版本…

FreeRTOS动态任务和静态任务创建

一.动态任务创建 1.搭建任务框架 去task.c中将任务参数复制到main中 然后将const去掉,它会限制参数类型,任务大小、任务优先级、任务句柄需要去宏定义,任务句柄是指针类型要取地址 vTaskStartScheduler(); //开启任务调度,.c…

QT:Graphics View的坐标系介绍

在 Qt 的 Graphics View 框架中,存在三种不同的坐标系,分别是 物品坐标系(Item Coordinates)、场景坐标系(Scene Coordinates) 和 视图坐标系(View Coordinates)。这三种坐标系在图形…

C# httpclient 和 Flurl.Http 的测试

关于C#调用接口或Post,Flurl封装了httpclient, CSDN有哥们提供了一个公网的测试网站,可以测试Post调用,我写了2个函数,测试httpclient和Flurl使用Post: async 和 await 是成对使用的,为了接受web异步返回的数据,winfor…