JVM内存分配:堆、栈和方法区
摘要:基本类型的变量、对象的引用和函数调用的现场等存储在栈中,通过new关键字和构造器创建的对象存储在堆中,字面量如100、”hello”和常量等存储在静态区。
概述
我们首先介绍一个本文要经常提及的术语字面量(literal)。在计算机科学中,字面量是用于表达源代码中一个固定值的表示法(notation)。几乎所有计算机编程语言都具有对基本类型变量值的字面量表示,诸如整数、浮点数以及字符串,其中常见的字符串字面量是指双引号引起来的一系列字符,双引号中可以没有字符,可以只有一个字符,也可以有很多字符。
int a = 100; // 100为int类型字面量
String bStr = "Hello world" // Hello world 为字符串字面量
字面量作为一种通用的,跨平台的数据交换格式,在程序界是公认的事实。
言归正传,下面聊聊本文的主角。JVM中内存分配一共有三个区:堆区(heap)、栈区(stack)和方法区(静态区),了解java的这3大区域非常有必要,尤其是工作中需要对jvm性能调优,更应该深度掌握下它们的概念和作用。
堆区
堆是一个运行时数据区,专门用来保存类(class)的实例(instance)并分配空间,不存放基本类型和对象引用,例如new 创建的实例和数组,实际上只是保存实例的字面量、类型和类型标记等,然而并不保存实例的方法(方法是指令,保存在下一节介绍的栈中)。
jvm只有一个堆区,它被所有线程共享。堆的大小是由垃圾收集器来负责的,优势是可以动态地分配内存大小,生命周期也不必事先告诉编译器,因为它是在运行时动态分配内存的,垃圾收集器会自动收走不再使用的数据。缺点是由于要在运行时动态分配内存,所以,存取速度较慢。
栈区
每个线程包含一个栈区,栈中只保存基础数据类型变量的字面量和自定义对象的引用(不是对象),对象都存放在堆区中。
每个栈中的数据(基本类型和对象引用)都是私有的,其它栈不能访问。栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
方法区
方法区又叫静态区,跟堆一样,被所有的线程共享。它存储的都是在整个程序中永远唯一的元素,如class和static变量。全局变量和静态成员变量的存储是放在一块的,但是,初始化和未初始化的分别存储在相邻的两块区域。
虚拟机的体系结构包括堆、方法区、本地方法栈和pc寄存器。而方法区保存的就是一个类的模板,堆是放类的实例的,一般来用于函数计算,它里面的数据在函数执行完时是不会存储的,直接丢弃。这就是为什么局部变量每一次都是一样的,即便修改了它后,下次执行函数的时候还是原来的值。
如果栈内存或者堆内存不足都会抛出异常:
栈空间不足:java.lang.StackOverFlowError。
堆空间不足:java.lang.OutOfMemoryError。
栈的空间大小远远小于堆的。栈空间操作起来最快但是空间很小,通常大量的对象都是放在堆空间,理论上整个内存没有被其它进程使用的空间甚至硬盘上的虚拟内存都可以被当成堆空间来使用。
为什么要把堆和栈区分出来呢?栈中不是也可以存储数据吗?
第一,从软件设计的角度看,栈代表了处理逻辑,而堆代表了数据。这样分开,使得处理逻辑更为清晰,体现了分而治之的思想,这种隔离、模块化的思想在软件设计的方方面面都有体现。
第二,堆与栈的分离,使得堆中的内容可以被多个栈共享(也可以理解为多个线程访问同一个对象)。这种共享有很多益处。一方面,它提供了一种有效的数据交互方式(如:共享内存),另一方面,堆中的共享常量和缓存可以被所有栈访问,节约空间。
第三,栈因为运行时的需要,比如保存系统运行的上下文,需要进行地址段的划分。由于栈只能向上增长,因此就会限制栈存储内容的能力。而堆不同,堆中的对象可以根据需要动态调整,因此栈和堆的拆分,使得动态增长成为可能,相应栈中只需记录堆中的一个地址即可。
第四,面向对象就是堆和栈的完美结合。其实,面向对象方式的程序与以前结构化的程序在执行上没有任何区别。但是,面向对象的引入,使得对待问题的思考方式发生了改变,而更接近于自然方式的思考。当我们把对象拆开时就会发现对象的属性其实就是数据,存放在堆中;而对象的行为(方法),就是算法(运行逻辑),放在栈中。我们在编写对象的时候,其实即编写了数据结构,也编写的处理数据的算法。不得不承认,面向对象的设计确实极具曲线美。
知行合一
下面分析一下基础类型变量内存分配过程。
int a = 3;
int b = 3;
编译器处理int a = 3时,首先,它会在栈中创建一个变量为a的引用;然后,查找栈中是否有3这个字面量,如果没找到,就将3存放进来,然后将a指向3。接着处理int b = 3,在创建完b的引用变量后,因为在栈中已经有字面量3,便将b直接指向3。这样,就出现了a与b均指向3的情况。
这时,如果再令a=4,那么编译器会重新搜索栈中是否有字面量4,如若没有,则将4存放进来,并令a重新指向4;如果已经有了,则直接将a重新指向它。故改变a值的时候不会影响到b的值。要注意栈中字面量的共享与两个对象的引用同时指向一个堆中对象的共享是不同的,因为基础类型变量a的修改并不会影响到b,它是由编译器完成的,目标是节省空间;而一个对象引用变量修改了堆中对象的内部状态时,会影响其它共同引用了堆中对象的变量。
String bStr = "Hello world";
上面的语句中变量bStr放在栈上,用new创建出来的字符串对象放在堆上,而“Hello world”这个字面量放在静态区。
Reference
- https://blog.csdn.net/jianesrq0724/article/details/80309690
- https://blog.csdn.net/gaoyong_stone/article/details/79540242
JVM内存分配:堆、栈和方法区的更多相关文章
- jvm内存模型中-栈,方法区,程序计数器是线程安全的
文章转自 https://www.cnblogs.com/myna/p/7567889.html 引文 JDK7及之前版本的方法区(Method Area)和Java堆一样,是各个线程共享的内存区域 ...
- Java虚拟机垃圾回收:内存分配与回收策略 方法区垃圾回收 以及 JVM垃圾回收的调优方法
在<Java对象在Java虚拟机中的创建过程>了解到对象创建的内存分配,在<Java内存区域 JVM运行时数据区>中了解到各数据区有些什么特点.以及相关参数的调整,在<J ...
- JVM内存分配策略、各个代区、FullGC/MinorGC
主要讨论默认的Serial/Serial Old内存分配: 一.几种分配方案 1. 对象优先在Eden分配: 一般情况下,对象会在新生代的Eden区分配,Eden区没有足够空间时,虚拟机会 发起一次 ...
- 转:JVM 内存初学 (堆(heap)、栈(stack)和方法区(method) )
原文地址:JVM 内存初学 (堆(heap).栈(stack)和方法区(method) ) 博主推荐 深入浅出JVM 这本书 先了解具体的概念:JAVA的JVM的内存可分为3个区:堆(heap).栈( ...
- JVM内存结构之堆、栈、方法区以及直接内存、堆和栈区别
JVM内存结构之堆.栈.方法区以及直接内存.堆和栈区别 一. 理解JVM中堆与栈以及方法区 堆(heap):FIFO(队列优先,先进先出):二级缓存:*JVM中只有一个堆区被所有线程所共享:对象和数 ...
- JVM内存模型——堆(heap)、栈(stack)和方法区(method)
JAVA的JVM的内存可分为3个区:堆(heap).栈(stack)和方法区(method) 堆区:堆内存用于存放由new创建的对象和数组.堆是JVM管理的内存中最大的一块,堆被所有线程共享,目的 ...
- JVM内存的堆、栈和方法区
JVM的内存分为堆.栈.方法区和程序计数器4个区域 存储内容:基本类型,对象引用,对象本身,class,常量,static变量 堆: 拥有者:所有线程 内容:对象本身,不存放基本类型和对象引用 垃圾回 ...
- java内存管理(堆、栈、方法区)
java内存管理 简介 首先我们要了解我们为什么要学习java虚拟机的内存管理,不是java的gc垃圾回收机制都帮我们释放了内存了吗?但是在写程序的过程中却也往往因为不懂内存管理而造成了一些不容易察觉 ...
- JVM虚拟机(二):堆、栈、方法区概念区别
Java 堆 Java堆是和Java应用程序关系最密切的内存空间,几乎所有的对象都放在其中,并且Java堆完全是自动化管理,通过垃圾收集机制,垃圾对象会自动清理,不需自己去释放. 根据垃圾回收机制的不 ...
- jvm 堆、栈 、方法区概念和联系
一.三者联系 1.堆:解决数据的存储问题( 即 数据怎么放,放到哪 ). 2.栈:解决程序运行的问题( 即 程序如何执行,或者说如何处理数据 ). 3.方法区:辅助堆栈的一块永久区,解决堆栈信息的产生 ...
随机推荐
- FormCreate中在事件中获取api
form-create中在事件中获取api FormCreate 是一个可以通过 JSON 生成具有动态渲染.数据收集.验证和提交功能的表单生成组件.支持5个UI框架,并且支持生成任何 Vue 组件. ...
- 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
图表图片由阿里巴巴提供 本文作者的观点:QwQ-32B 作为小型开源 AI 模型,在数学.编程等任务上表现接近甚至超越 DeepSeek-R1 671B模型,同时计算资源占用大幅降低,使其更易部署和使 ...
- HarmonyOS Next 鸿蒙开发-如何使用服务端下发的RSA公钥(字符串)对明文数据进行加密
如何使用服务端下发的RSA公钥(字符串)对明文数据进行加密 将服务器下发的RSA公钥字符串替换掉pubKeyStr即可实现,具体可参考如下代码: import { buffer, util } fro ...
- php 过滤掉emoji表情
<?php function filter_emoji($str) { $str = preg_replace_callback( //执行一个正则表达式搜索并且使用一个回调进行替换 '/./u ...
- composer remove 卸载依赖
remove 命令 remove 命令用于移除一个包及其依赖(在依赖没有被其他包使用的情况下),如果依赖被其他包使用,则无法移除: $ composer remove monolog/monolog ...
- 视觉SLAM十四讲——有关相机运动的汇报
视觉SLAM十四讲--有关相机运动的汇报 大概用了一个月的时间看完slam十四讲,里面很多内容算是填坑了很多以前遇到的不懂的点,并且脑海里也大致有了一个关于SLAM的框架,现在就这篇文章将其中相机运动 ...
- MySQL-脏页的刷新机制
MySQL内存结构-缓冲区 MySQL的缓冲区中有数据页,索引页,插入缓冲等等,这个角度是从页的功能来分类的.本小节从另一个视角关注这些页,如果从 是否被修改过(和磁盘不一致) 这个角度来区分这些页, ...
- javascript 字符串截取
<script> //字符截取(需要的字符长度) function cut_str(need_str_length){ var bag_set = document.getElem ...
- 高格发票勾稽之BUG
select (INVNO) AS INVCODE, SDATE ,* FROM STKSALE1 WHERE LEN(INVSCODE) > 0 AND INVSCODE = '3100000 ...
- MCP (Model Context Protocol)初体验:企业数据与大模型融合初探
简介 模型上下文协议(Model Context Protocol,简称MCP)是一种创新的开放标准协议,旨在解决大语言模型(LLM)与外部数据和工具之间的连接问题.它为AI应用提供了一种统一.标准化 ...