Java虚拟机——内存的分配详解

内存区域划分

        对于大多数的程序员来说,Java 内存比较流行的说法便是堆和栈,这其实是非常粗略的一种划分,这种划分的“堆”对应内存模型的 Java 堆,“栈”是指虚拟机栈,然而 Java 内存模型远比这更复杂,想深入了解 Java 的内存,还是有必要明白整个内存区域分。

        了解 Java GC 机制,必须先清楚在 JVM 中内存区域的划分。 在 Java 运行时的数据区里,由 JVM 管理的内存区域分为下图几个模块:

JVM 内存划分

程序计数器(Program Counter Register)

        程序计数器是一个比较小的内存区域,用于指示当前线程所执行的字节码执行到了第几行,可以理解为是当前线程的行号指示器。 字节码解释器在工作时,会通过改变这个计数器的值来取下一条语句指令。

        每个程序计数器只用来记录一个线程的行号,所以它是线程私有(一个线程就有一个程序计数器)的。

        如果程序执行的是一个 Java 方法,则计数器记录的是正在执行的虚拟机字节码指令地址;如果正在执行的是一个本地( native,由 C 语言编写完成)方法,则计数器的值为 Undefined,由于程序计数器只是记录当前指令地址,所以不存在内存溢出的情况,因此,程序计数器也是所有JVM内存区域中唯一一个没有定义 OutOfMemoryError 的区域。

虚拟机栈(JVM Stack)

        一个线程的每个方法在执行的同时,都会创建一个栈帧(Statck Frame),栈帧中存储的有局部变量表、操作数栈、动态链接、方法出口等,当方法被调用时,栈帧在 JVM 栈中入栈,当方法执行完成时,栈帧出栈。

        局部变量表中存储着方法的相关局部变量,包括各种基本数据类型,对象的引用,返回地址等。 在局部变量表中,只有 long 和 double 类型会占用 2 个局部变量空间(Slot,对于32位机器,一个 Slot 就是 32 个 bit),其它都是 1 个 Slot。 需要注意的是,局部变量表是在编译时就已经确定好的,方法运行所需要分配的空间在栈帧中是完全确定的,在方法的生命周期内都不会改变。

        虚拟机栈中定义了两种异常,如果线程调用的栈深度大于虚拟机允许的最大深度,则抛出 StatckOverFlowError(栈溢出);不过多数 Java 虚拟机都允许动态扩展虚拟机栈的大小(有少部分是固定长度的),所以线程可以一直申请栈,直到内存不足,此时,会抛出 OutOfMemoryError(内存溢出)。

        每个线程对应着一个虚拟机栈,因此虚拟机栈也是线程私有的。

本地方法栈(Native Method Statck)

        本地方法栈在作用,运行机制,异常类型等方面都与虚拟机栈相同,唯一的区别是:虚拟机栈是执行 Java 方法的,而本地方法栈是用来执行 native 方法的,在很多虚拟机中(如:Sun 的 JDK 默认的 HotSpot 虚拟机),会将本地方法栈与虚拟机栈放在一起使用。

        本地方法栈也是线程私有的。

堆区(Heap)

        堆区是理解 Java GC 机制最重要的区域,没有之一。 在 JVM 所管理的内存中,堆区是最大的一块,堆区也是 Java GC 机制所管理的主要内存区域,堆区由所有线程共享,在虚拟机启动时创建。 堆区的存在是为了存储对象实例,原则上讲,所有的对象都在堆区上分配内存(不过现代技术里,也不是这么绝对的,也有栈上直接分配的)。

        一般的,根据 Java 虚拟机规范规定,堆内存需要在逻辑上是连续的(在物理上不需要),在实现时,可以是固定大小的,也可以是可扩展的,目前主流的虚拟机都是可扩展的。 如果在执行垃圾回收之后,仍没有足够的内存分配,也不能再扩展,将会抛出 OutOfMemoryError:Java heap space 异常。

        关于堆区的内容还有很多,将在下面“内存分配机制”中详细介绍。

方法区(Method Area)

        在 Java 虚拟机规范中,将方法区作为堆的一个逻辑部分来对待,但事实上,方法区并不是堆(Non-Heap);另外,不少人的博客中,将 Java GC 的分代收集机制分为 3 个代:青年代,老年代,永久代,这些作者将方法区定义为“永久代”,这是因为,对于之前的 HotSpot Java 虚拟机的实现方式中,将分代收集的思想扩展到了方法区,并将方法区设计成了永久代。 不过,除 HotSpot 之外的多数虚拟机,并不将方法区当做永久代,HotSpot 本身,也计划取消永久代。 本文中,由于主要使用 Oracle JDK6.0,因此仍将使用永久代一词。

        方法区是各个线程共享的区域,用于存储已经被虚拟机加载的类信息(即加载类时需要加载的信息,包括版本、field、方法、接口等信息)、final 常量、静态变量、编译器即时编译的代码等。

        方法区在物理上也不需要是连续的,可以选择固定大小或可扩展大小,并且方法区比堆还多了一个限制:可以选择是否执行垃圾收集。 一般的,方法区上执行的垃圾收集是很少的,这也是方法区被称为永久代的原因之一(HotSpot),但这也不代表着在方法区上完全没有垃圾收集,其上的垃圾收集主要是针对常量池的内存回收和对已加载类的卸载。

        在方法区上进行垃圾收集,条件苛刻而且相当困难,效果也不令人满意,所以一般不做太多考虑,可以留作以后进一步深入研究时使用。

        在方法区上定义了 OutOfMemoryError:PermGen space 异常,在内存不足时抛出。

  • 运行时常量池(Runtime Constant Pool)

        方法区的一部分,用于存储编译期就生成的字面常量、符号引用、翻译出来的直接引用(符号引用就是编码是用字符串表示某个变量、接口的位置,直接引用就是根据符号引用翻译出来的地址,将在类链接阶段完成翻译);运行时常量池除了存储编译期常量外,也可以存储在运行时间产生的常量(比如 String 类的 intern() 方法,作用是 String 维护了一个常量池,如果调用的字符 “abc” 已经在常量池中,则返回池中的字符串地址,否则,新建一个常量加入池中,并返回地址)。

直接内存(Direct Memory)

        直接内存并不是 JVM 管理的内存,可以这样理解,直接内存,就是 JVM 以外的机器内存。

        比如:你有 4G 的内存,JVM占用了1G,则其余的 3G 就是直接内存,JDK 中有一种基于通道(Channel)和缓冲区(Buffer)的内存分配方式,将由 C 语言实现的 native 函数库分配在直接内存中,用存储在 JVM 堆中的 DirectByteBuffer 来引用。 由于直接内存受到本机器内存的限制,所以也可能出现 OutOfMemoryError 的异常。

内存分配机制

        以下面代码为例,来分析,Java 的实例对象在内存中的空间分配。

//JVM 启动时将 Person.class 放入方法区
public class Person {//new Person 创建实例后,name 引用放入堆区,name 对象放入常量池private String name;//new Person 创建实例后,age = 0 放入堆区private int age;//Person 方法放入方法区,方法内代码作为 Code 属性放入方法区public Person(String name, int age) {this.name = name;this.age = age;}//toString 方法放入方法区,方法内代码作为 Code 属性放入方法区@Overridepublic String toString() {return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';}
}
   //JVM 启动时将 Test.class 放入方法区
public class Test {//main 方法放入方法区,方法内代码作为 Code 属性放入方法区public static void main(String[] args) {//person1 是引用放入虚拟机栈区,new 关键字开辟堆内存 Person 自定义对象放入堆区Person person1 = new Person("张三", 18);Person person2 = new Person("李四", 20);//通过 person 引用创建 toString() 方法栈帧person1.toString();person2.toString();}
}

1、首先 JVM 会将 Test.class, Person.class 加载到方法区,找到有 main() 方法的类开始执行。

JVM 内存划分 实例1

如上图所示,JVM 找到 main() 方法入口,创建 main() 方法的栈帧放入虚拟机栈,开始执行 main() 方法。

Person person1 = new Person("张三", 18);

执行到这句代码时,JVM 会先创建 Person

实例放入堆区,person2 也同理。

2、创建完 Person 两个实例,main() 方法中的 person1,person2 会指向堆区中的 0x001,0x002(这里的内存地址仅作为示范)。紧接着会调用 Person 的构造函数进行赋值,如下图:

JVM 内存划分 实例2

如上图所示,新创建的的 Person 实例中的 name, age 开始都是默认值。 调用构造函数之后进行赋值,name 是 String 引用类型,会在常量池中创建并将地址赋值给 name,age 是基本数据类型将直接保存数值。

注:Java 中基本类型的包装类的大部分都实现了常量池技术,这些类是 Byte, Short, Integer, Long, Character, Boolean,另外两种浮点数类型的包装类则没有实现。

基本数据类型包装类 (是否实现了常量池技术)
byteByte 是
booleanBoolean 是
shortShort 是
charCharacter 是
intInteger 是
longLong 是
floatFloat 否
doubleDouble 否

3、Person 实例初始化完后,执行到 toString() 方法,同 main() 方法一样 JVM 会创建一个 toString() 的栈帧放入虚拟机栈中,执行完之后返回一个值。

JVM 内存划分 实例3

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

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

相关文章

【计算机毕业设计】游戏售卖网站——后附源码

🎉**欢迎来到琛哥的技术世界!**🎉 📘 博主小档案: 琛哥,一名来自世界500强的资深程序猿,毕业于国内知名985高校。 🔧 技术专长: 琛哥在深度学习任务中展现出卓越的能力&a…

数据结构和算法(哈希表和图(A*算法精讲))

一 、哈希表 1.1 哈希表原理精讲 哈希表-散列表,它是基于快速存取的角度设计的,也是一种典型的“空间换时间”的做法 键(key): 组员的编号如,1、5、19。。。 值(value): 组员的其它信息(包含性别、年龄和…

Python-VBA函数之旅-divmod函数

目录 1、divmod函数: 1-1、Python: 1-2、VBA: 2、相关文章: 个人主页:非风V非雨-CSDN博客 divmod函数在Python中具有广泛的应用场景,特别是在需要同时处理除法的商和余数的情况下。常见的应用场景有&a…

《系统架构设计师教程(第2版)》第9章-软件可靠性基础知识-04-软件可靠性设计

文章目录 1. 容错设计技术1.1 恢复块设计1.2 N版本程序设计1.3 冗余设计 2. 检错技术3. 降低复杂度设计4. 系统配置中的容错技术4.1 双机热备技术4.1.1 双机热备模式4.1.2 双机互备模式4.1.3 双机双工 4.2 服务器集群技术 1. 容错设计技术 1.1 恢复块设计 恢复块设计 选择一组…

Maven:<dependencyManagement>:依赖集中管理

dependencyManagement Maven &#xff1c;dependencyManagement&#xff1e;&#xff0c;请介绍一下 在Apache Maven构建工具中&#xff0c;<dependencyManagement> 是一个非常重要的元素&#xff0c;用于在一个项目或一组项目的顶级POM&#xff08;Project Object Model…

TCP/IP协议—TCP

TCP/IP协议—TCP TCP协议TCP通信特点TCP技术概念TCP定时器 TCP头部报文TCP连接三次握手&#xff08;建立连接&#xff09;四次挥手&#xff08;释放连接&#xff09;连接状态 TCP协议 传输控制协议&#xff08;TCP&#xff0c;Transmission Control Protocol&#xff09;是一种…

Springboot集成Ehcache3实现本地缓存

如果只需要在单个应用程序中使用本地缓存&#xff0c;则可以选择Ehcache&#xff1b;它支持内存和磁盘存储&#xff0c;这里不以注解方式演示&#xff0c;通过自己实现缓存管理者灵活控制缓存的读写&#xff1b; 1、引入相关依赖 <!-- ehcache3集成start --><depende…

苹果在中国市场衰退,全球市场跌幅最大,难怪慌忙大降价

日前市调机构IDC公布了今年一季度全球市场的手机品牌排名&#xff0c;数据显示苹果的跌幅最大&#xff0c;说明它不仅在中国市场衰退&#xff0c;在全球市场也出现衰退&#xff0c;如此也就不奇怪苹果史无前例的在3月份对iPhone15降价1500元促销了。 数据显示一季度苹果的出货量…

阿里云服务器公网带宽按固定和按使用流量怎么选?哪个优惠?

阿里云服务器的公网带宽计费模式分为“按固定带宽”和“按使用流量”&#xff0c;有什么区别&#xff1f;按固定带宽是指直接购买多少M带宽&#xff0c;比如1M、5M、10M、100M等&#xff0c;阿里云直接分配用户所购买的带宽值&#xff0c;根据带宽大小先付费再使用&#xff1b;…

k8s控制器(五)_____DaemonSet

DaemonSet控制器 DaemonSet控制器是Kubernetes中的一种控制器&#xff0c;用于确保集群中的每个节点都运行一个Pod的副本。它通常用于在整个集群中部署一些系统级别的服务&#xff1a; 在每一个node节点运行一个存储服务&#xff0c;例如gluster&#xff0c;ceph。在每一个no…

数据可视化高级技术Echarts(桑基图入门)

目录 一、什么是桑基图 二、基本特征 三、设计注意事项 四、使用Echarts进行初级绘制 1.首先不能忘记五个基本步骤 2.绘制的时需要将图像类型series.type设定为sankey类型。 一、什么是桑基图 桑基图&#xff08;Sankey diagram&#xff09;&#xff0c;即桑基能量分流图&…

2024很漂亮的个人主页HTML源码

源码介绍 很漂亮的个人主页HTML源码&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面 截图效果 源码下载 很漂亮的个人主页HTML源码

[大模型]浦语灵笔图文理解创作

浦语灵笔图文理解&创作 环境准备 首先在 AutoDL 上租一台显卡驱动支持 11.7 以上的双卡 3090 机器. 在选择镜像是选择 Miniconda --> conda3 --> 3.8(ubuntu20.04)–> 11.6 打开 jupyter lab 中的终端&#xff0c;首先运行以下命令安装 PyTorch 2.0.1 # 升级pi…

1043: 利用栈完成后缀表达式的计算

解法&#xff1a; #include<iostream> #include<stack> using namespace std; int main() {char a;stack<int> sk;while (cin >> a && a ! #) {if (a > 0 && a < 9) {sk.push(a - 0);}else {int num2 sk.top();sk.pop();int n…

数字乡村创新实践探索农业现代化与乡村振兴新路径:科技赋能农村全面振兴与农民福祉新纪元

目录 引言 一、数字乡村与农业现代化新路径 1、智慧农业引领农业现代化 2、农业产业链的数字化转型 二、数字乡村与乡村振兴新路径 1、农村信息化水平的提升 2、农村治理模式的创新 三、科技赋能农村全面振兴与农民福祉新纪元 1、提升农业生产效益与农民收入 2、促进…

H.265视频直播点播录像EasyPlayer.js流媒体播放器用户常见问题及解答

EasyPlayer属于一款高效、精炼、稳定且免费的流媒体播放器&#xff0c;可支持多种流媒体协议播放&#xff0c;无须安装任何插件&#xff0c;起播快、延迟低、兼容性强&#xff0c;使用非常便捷。 今天我们来汇总下用户常见的几个问题及解答。 1、EasyPlayer.js播放多路H.265视…

.NET Framework安装失败的原因及解决方法

.NET Framework安装失败的原因及解决方法 大家好我是艾西&#xff0c;一个做服务器租用的游戏爱好者兼网络架构系统环境问题网络工具人。在我们平时使用PC安装某些程序会出现.NET Framework缺失的提示&#xff0c;那么也会有很多的小伙伴搞不懂什么原因导致的&#xff0c;这个问…

hadoop编程之词频统计

数据集实例 java代码&#xff0c;编程 实例 我们要先创建三个类分别为WordCoutMain、WordCoutMapper、WordCoutReducer这三个类 对应的代码如下 WordCoutMain import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Pat…

wps导出pdf文献引用不能跳转解决办法

问题描述 本科论文参考文献使用wps设置交叉引用&#xff0c;但导出pdf后无法跳转引用 尝试 用office word打开文件word版跳转没有问题&#xff0c; 另存为pdf或导出pdf。 但是pdf版跳转完全错误。 16跳到14.但是总体而言都是跳到包含该序号的页 要求不高的话也可以&#x…

文件上传App,H5,小程序多端兼容

插件地址&#xff1a;https://ext.dcloud.net.cn/plugin?id5459 下载lsj-upload插件 代码如下 结构 <lsj-upload :option"option" :size"size" :formats"formats" :debug"debug":instantly"instantly" change"…