【JVM】详解Java内存区域和分配

news2025/1/10 3:29:40

这里写目录标题

  • 一、前言
  • 二、运行时数据分区
    • 2.1程序计数器(PC)
    • 2.2 Java虚拟机栈
    • 2.3 本地方法栈
    • 2.4 Java堆
    • 2.5 方法区
      • 2.5.1 运行时常量池
    • 2.6 直接内存
  • 三、HotSpot虚拟机对象探秘
    • 3.1 对象的创建
    • 3.2 对象的内存布局
    • 3.3 对象的访问定位

一、前言

C/C++需要自行回收和释放已经没用的对象,但是对于Java程序员来说,在虚拟机自动内存管理机制的帮助下, 不再需要为每一个new操作去写对应的free代码,就如同吃完饭之后不需要自己收拾盘子一样。看起来一切都师范的美好,但是一旦出现内存泄漏和溢出,如果不了解虚拟机是如何使用内存的,那么排查错误、纠正问题就会是一个艰难的问题。

二、运行时数据分区

Java虚拟机再执行的时候,会将它管理的内存划分为若干个不同的数据区,这些区域各有用途,本节就是了解这些内容。根据Java虚拟机规范,JVM所管理的内存将会包括以下几个运行时区域
在这里插入图片描述

2.1程序计数器(PC)

程序计数器是一块较小的内存空间,是当前所执行的指令的行号指示器,在JVM中,字节码解释器工作是就是通过改变这个计数器来选取下一条需要执行的字节码指令,一系列分支、循环、跳转和异常处理、线程恢复都需要使用到PC。需要注意到的是,这个程序计数器是线程私有的,用于标识当前线程运行到的指令位置,它和计算机自身的PC不一样,计算机的PC只有一个,用于表示整个计算机运行到的指令的位置,而JVM中的PC是指各个Java进程运行到的位置,每一个Java进程都有一个。

2.2 Java虚拟机栈

和程序计数器一样,Java虚拟机栈也是线程私有的,他的生命周期和线程相同。虚拟机栈是Java方法执行的线程内存模型:每个方法被执行的时候,JVM会在对应线程的虚拟机栈中创建一个栈帧。这个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息,每一个方法被调用直到执行完毕的过程,就对应着一个栈帧在虚拟机栈中入栈出栈的过程。方法的调用和结束调用类似于栈的入栈和出栈,因此虚拟机栈中栈帧的出入栈和方法的调用是保持一致的。

局部变量表存放着许多编译器就可知的JVM基本数据类型(boolean, byte, char, short, int, float, long, double)以及对象引用、return Address类型。这些数据类型在局部变量表中以局部变量槽Slot表示,其中long和double作为64B的变量会占用两个Slot,其余的会占用一个

在虚拟机栈中,如果线程请求的栈深度大于虚拟机允许的深度,则会抛出StackOverflowError异常;如果JVM栈可以动态扩展,但是扩展时服务申请到足够多的内存,则会抛出OutOfMemoryError异常。

2.3 本地方法栈

本地方法栈和虚拟机栈发挥的作用十分相似,区别是虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则是为虚拟机使用到本地方法服务。

2.4 Java堆

Java堆事JVM的内存管理中最大的一块,是一个线程共享的区域,JVM启动时就会创建这片区域,所有的对象实例以及数组都应该在堆上分配,但是随着Java语言的发展,现在已经看到了一些迹象表明日后可能支持将值存放在堆上,并且由于即时编译技术的进步,栈上分配、标量替换优化手段使得将其他内容存放在堆上成为可能,因此还需要密切关注技术变化。

堆也是垃圾收集器管理的内存区域,对于垃圾回收期GC的详细介绍,会在后面详细介绍,也可以先看看这个
https://blog.csdn.net/qq_25430563/article/details/109201945

2.5 方法区

方法区和堆一样,是各个线程共享的内存区域,用于存储已经被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

在以前HotSpot设计团队选择把垃圾收集器的分区设计扩展至方法区,使用管理永久代的策略管理方法区,使得垃圾收集器能够像管理堆一样管理这部分内存,省去专门为方法区编写内存管理代码的工作。这使得JDK 8之前的程序员喜欢用永久代来称呼方法区,但实际上两者是不一样的,在《JMV规范》中并没有统一要求方法区需要使用何种垃圾收集策略,而常用的HotSpot虚拟机则在JDK8之前都是将方法区作为永久代进行管理,但是将方法区视作为永久代并不是一个好主意,这会导致Java更容易遇到内存溢出的问题,HotSpot从JDK6逐渐开始放弃永久代,改为使用本地内存实现方法区,在JDK8完全放弃了永久代的概念,改用在本地内存中实现元空间来替代。

2.5.1 运行时常量池

运行时常量池是方法区了一部分,class文件除了有类的版本,字段方法,接口等描述信息外,还有一项信息就是常量池表,用于存放编译期生成的各种字面量和符号引用,常量池表会在类加载后放入运行时常量池。

运行时常量池相对于Class文件中的常量池具有动态性,运行期间可以将新的常量放入到池中,这种特性用的较多的是String的intern()方法

2.6 直接内存

直接内存并不是JVM数据区的一部分,他就是平时我们讲的一般的内存空间

直接内存与堆内存的区别:
直接内存申请空间耗费很高的性能,堆内存申请空间耗费比较低
直接内存的IO读写的性能要优于堆内存,在多次读写操作的情况相差非常明显

三、HotSpot虚拟机对象探秘

本节主要探讨HotSpot虚拟机在Java堆中对象分配,布局和访问的全过程

3.1 对象的创建

当虚拟机碰到一条字节码new指令的时候,首先去检查这个指令是否能在常量池中定义到一个类的符号引用(也就是检查有没有这个类),并且检查这个类是否被加载、解析和初始化过。如果没有则先执行类加载过程。在类加载检查通过后,接下来虚拟机将为新生的对象分配内存,对象所需内存大小在类加载完成后可以完全确定,为对象分配空间实际上是等同于将一块确定大小的内存块从堆内划分出来。

如果堆中内存块是绝对规整的,那么就会将所有已经使用的内存放在边,没有使用的内存放在另一边,管理内存只需要一个指针,分配内存只是将指针向空闲空间方向挪动相同大小的距离,这种方式称为指针碰撞,Serial,ParNew等带压缩整理过程的收集器会使用此法,简单高效。另外如果堆中内存块大小不一,那么已使用和空闲的内存相互交错,JVM就需要维护一个列表,记录哪个内存块是可用的,称为空闲列表法,CMS等基于清除算法的收集器会使用该法

另外还有一个问题就是:对象创建在虚拟机中时十分频繁的过程,因此需要考虑并发问题。这有两种方式解决问题:一个是对分配内存空间的动作进行同步处理,也就是保证分配内存空间的操作的原子性。另外一个就是为各个线程预先分配一小块内存,称为本地线程分配缓冲(TLAB),某个线程需要给对象实例分配内存,则先分配到TLAB中,只有本地缓冲区用完了,才会同步锁定。

接下来JVM还需要对对象进行必要的设置,比如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象哈希码和对象的GC分代年龄信息等。完成这些共走后,从虚拟机的视角来看,一个新的对象已经产生了,但是从Java程序视角来看,对象创建才刚刚开始,构造函数(也就是Class文件中的<init>()方法)还没开始执行,所有字段都默认为零值。

3.2 对象的内存布局

在HotSpot虚拟机中,对象在堆内存中的存储布局可以划分为三个部分:对象头(header),实例数据(instance data)和对齐填充(padding)。

HotSpot虚拟机对象的对象头部分包括两类信息。第一类是用于存储对象自身的运行时数据,如哈 希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。对象头的另外一部分是类型指针,即对象指向它的类型元数据的指针,Java虚拟机通过这个指针来确定该对象是哪个类的实例。这是Java实现反射的重要组成部分

接下来实例数据部分是对象真正存储的有效信息,即我们在程序代码里面所定义的各种类型的字 段内容,无论是从父类继承下来的

第三部分是对齐填充,HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,因此对齐填充用于填充空余的字节,使得一个对象的大小是8字节的整数倍。

3.3 对象的访问定位

创建对象自然是为了后续使用该对象,Java程序会通过栈上的reference数据来操作堆上的具体对象。对象访问方式也是由虚拟机实现而定的,主流的访问方式主要有使用句柄和直接指针两种:

如果使用句柄访问,Java堆中将划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自具体的地址信息。
在这里插入图片描述

如果使用直接指针访问,Java堆中对象的内存布局就必须考虑如何放置访问类型数据的相关信息,reference中存储的直接就是对象地址,如果只是访问对象本身的话,就不需要多一次间接访问的开销
在这里插入图片描述

使用句柄来访问的最大好处就是reference中存储的是稳定句柄地址,在对象被移动(垃圾收集时移动对象非常普遍)时只会改变句柄中的实例数据指针,但是需要额外一次的寻址开销;使用直接指针最大的好就是速度更快,它节省了一次指针定位的时间开销

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

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

相关文章

2023年 Java 发展趋势

GitHub 语言统计表明&#xff0c;Java在编程语言中排名第二&#xff0c;而在2022年的TIOBE指数中&#xff0c;Java排在第四。 抛开排名&#xff0c;Java是自诞生以来企业使用率最高的编程语言&#xff0c;作为一种编程语言&#xff0c;它比许多竞争对手都有更多的优点&#xf…

【C/C++】逗号表达式、算术运算符优先级

一、逗号表达式 1、如下图中代码&#xff0c;为变量d赋值&#xff0c;d的值为逗号表达式中的哪一个呢&#xff1f; 运行结果&#xff1a;d的值为6 2、再举个例子 运行结果&#xff1a;d的结果还是6 3、再举个例子 运行结果 以上面三种不同的逗号表达式为例&#xff0c;…

IronOCR 2023.3.2 Crack by Xacker

适用于 .NET 2023.3.2 的 IronOCR 提高了从 PDF 阅读文本时的可靠性。2023 年 3 月 2 日 - 10:22 新版本特征 添加了与 Amazon AWS (Amazon Linux) 的兼容性。添加了对各种旧版 Linux 发行版的兼容性。提高了从 PDF 阅读文本时的可靠性。创建可搜索的 PDF 时提高了速度和保真度…

JVM调优面试题——基础知识

文章目录1、JDK&#xff0c;JRE以及JVM的关系2、编译器到底干了什么事&#xff1f;3、类加载机制是什么&#xff1f;3.1、装载(Load)3.2、链接(Link)3.3、初始化(Initialize)4、类加载器有哪些&#xff1f;5、什么是双亲委派机制&#xff1f;6、介绍一下JVM内存划分&#xff08…

[蓝桥杯] 数学与简单DP问题

文章目录 一、简单数学问题习题练习 1、1 买不到的数目 1、1、1 题目描述 1、1、2 题解关键思路与解答 1、2 饮料换购 1、2、1 题目描述 1、2、2 题解关键思路与解答 二、DP问题习题练习 2、1 背包问题 2、1、1 题目描述 2、1、2 题解关键思路与解答 2、2 摘花生 2、2、1 题目…

收个滴滴Offer:从小伙三面经历,看看需要学点啥?

说在前面 在尼恩的&#xff08;50&#xff09;读者社群中&#xff0c;经常有小伙伴&#xff0c;需要面试大厂。 后续结合一些大厂的面试真题&#xff0c;给大家梳理一下学习路径&#xff0c;看看大家需要学点啥&#xff1f; 这里也一并把题目以及参考答案&#xff0c;收入咱…

Spring 容器创建初始化,获取bean流程分析

Spring 容器创建初始化&#xff0c;获取bean流程分析 Spring 容器创建初始化 流程分析 1、首先读取bean.xml 文件 2、扫描指定的包 com.hspedu.spring.component 2.1、扫描包&#xff0c;得到bean的class对象&#xff0c;排除包下不是bean的 2.2、扫描将bean信息封装BeanDef…

python之selenium库安装及用法(定位法、获取文本、文本框输入、鼠标点击、滑动滚动条)

一、selenium库安装 pip install selenium二、浏览器驱动安装 谷歌浏览器驱动下载地址&#xff1a;https://chromedriver.storage.googleapis.com/index.html 根据你电脑的谷歌浏览器版本&#xff0c;下载相应的就行。我下载的是110.0.5481.XX中的chromedriver_win32.zip 下载…

无公网IP快解析实现U+随时随地访问

现阶段商品从生产到消费者手中要经过多个环节&#xff0c;为实现对每一个环节进行管理&#xff0c;越来越多的企业选择通过信息化手段来实现。供应链管理系统配合供应链中各实体的业务需求&#xff0c;使操作流程和信息系统紧密配合&#xff0c;做到各环节无缝链接&#xff0c;…

【C++】string类的使用

目录 一、标准库中的string类 二、string类的常用接口 1、string类对象的常见构造 2、string类对象的容量操作 2.1、size 与 length 2.2、capacity 与 reserve 2.3、resize 2.4、总结 3、string类对象的访问及遍历操作 3.1、operator[] 与 at 3.2、begin end 3.3、…

Portraiture5人像磨皮润色修饰插件

Portraiture3和Portraiture4这两个版本大家用的比较多&#xff0c;那是因为这两个版本是中文比较全的版本。portraiture是一款强大的64位PS磨皮滤镜&#xff0c;利用该PS滤镜插件可以对图片中的人物进行润色&#xff0c;磨皮等操作&#xff0c;处理皮肤材质、头发等。帮您消除了…

HNU工训中心:平台 2HDL 语言与验证实验报告

一、自定 FSM 说明 1、状态描述 State0&#xff1a;睡觉&#xff0c;如果闹钟响则起床吃早餐&#xff0c;否则继续睡觉 State1&#xff1a;吃早餐&#xff0c;吃完去上课 State2&#xff1a;上课&#xff0c;上完课后如果要开会就去开会&#xff0c;否则去自习 State3&…

LiveGBS国标GB/T28181视频流媒体平台-功能视频集中录制存储云端录像H264|H265|HEVC视频存储

LiveGBS国标GB/T28181视频流媒体平台-视频集中录制存储云端录像H264|H265|HEVC视频存储1、云端录像存储2、手动配置录像2.1、按需录像2.2、一直录像3、录像计划3.1、录像计划入口3.2、新增录像计划3.3、编辑录像计划3.4、关联通道4、查看云端录像4.1、查看录像4.1.1、时间轴模式…

vue路由文件拆分管理

随着项目的原来越大&#xff0c;路由越来越多&#xff0c;我们的路由也会越来越多&#xff0c;如果都集中在一个文件中&#xff0c;会很冗杂文件很长。这时候我们可以将路由文件拆分&#xff0c;可读、方便管理。多人合作添加路由也能更多的避免代码冲突 代码拆分目录如图&…

18 客户端服务发现源码分析

Nacos客户端服务发现源码分析 总体流程 首先我们先通过一个图来直观的看一下&#xff0c;Nacos客户端的服务发现&#xff0c;其实就是封装参数、调用服务接口、获得返回实例列表。 但是如果我们要是细化这个流程&#xff0c;会发现不仅包括了通过NamingService获取服务列表&…

运筹系列79:使用Julia进行column generation求解

1. 案例建模 我们对cutting stock问题进行建模。rolls的尺寸为W&#xff0c;每个型号的需求量和尺寸分别为d和w&#xff0c;如下&#xff1a; struct Piecew::Float64d::Int endstruct Datapieces::Vector{Piece}W::Float64 endfunction Base.show(io::IO, d::Data)println(i…

hls.js如何播放m3u8文件(实例)?

HLS&#xff08;HTTP Live Streaming&#xff09;是一种视频流传输协议&#xff0c;是苹果推出的适用于iOS与macOS平台的流媒体传输协议。它将视频分割成若干个小段&#xff0c;每个小段大小一般为2~10秒不等&#xff0c;并通过HTTP协议进行传输。通过在每个小段之间插入若干秒…

C++学习笔记-多线程

传统的C&#xff08;C11之前&#xff09;中并没有引入线程这个概念&#xff0c;在C11出来之前&#xff0c;如果我们想要在C中实现多线程&#xff0c;需要借助操作系统平台提供的API&#xff0c;比如Linux的<pthread.h>&#xff0c;或者windows下的<windows.h> 。 …

数据结构——链表讲解(1)

作者&#xff1a;几冬雪来 时间&#xff1a;2023年3月3日 内容&#xff1a;数据结构链表讲解 目录 前言&#xff1a; 链表的概念&#xff1a; 1.为什么要有链表&#xff1a; 2.链表的运行原理&#xff1a; 3.链表的形态多少&#xff1a; 4.单链表的代码书写&#xff1…

QML定时器

QML使用Timer使用定时器 Timer 计时器可用于触发操作一次&#xff0c;或以给定的间隔重复触发。 常用属性&#xff1a; interval 设置触发器之间的间隔&#xff08;以毫秒为单位&#xff09;。 默认间隔为 1000 毫秒。 repeat 设置重复&#xff0c;为真&#xff0c;则以指定的…