java中栈内存与堆内存(JVM内存模型)

Java中堆内存和栈内存详解1 和 Java中堆内存和栈内存详解2 都粗略讲解了栈内存和堆内存的区别,以及代码中哪些变量存储在堆中、哪些存储在栈中。内存中的堆和栈到底是什么 详细讲述了程序在内存中的模型,从可执行文件(ELF)格式的编译介绍了堆和栈,主要是C/C++语言,讲的比较清楚,借鉴性比较强。

其实,对于java语言,编译后的文件是一个中间字节代码,操作系统不能直接执行,需要jvm解释执行。与C/C++对应起来,理解java的栈内存和堆内存,应该从jvm的内存模型入手(参考深入理解JVM-内存模型(jmm)和GC)。

一、Java内存模型

java程序内存的分配是在JVM虚拟机内存分配机制下完成。

java内存模型(Java Memory Model ,JMM)就是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。

根据java虚拟机规范,java虚拟机管理的内存将分为下面五大区域。

1.程序计数器

程序计数器(Program Counter Register)是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的方式去实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现,也就是说,在同一时刻一个处理器内核只会执行一条线程,处理器切换线程时并不会记录上一个线程执行到哪个位置,所以为了线程切换后依然能恢复到原位,每条线程都需要有各自独立的程序计数器。

特点:

线程私有

JVM规范中唯一没有规定OutOfMemoryError情况的区域

如果正在执行的是Native 方法,则这个计数器值为空

2. java栈(虚拟机栈)(具体参考JVM系列-内存区域- Java虚拟机栈(三))

Java 虚拟机栈(Java Virtual Machine Stacks)是线程私有的,生命周期随着线程,线程启动而产生,线程结束而消亡。

Java 虚拟机栈描述的是 Java 方法执行的内存模型,用于存储栈帧。线程启动时会创建虚拟机栈,每个方法在执行时会在虚拟机栈中创建一个栈帧,用于存储局部变量表、操作数栈、动态连接、方法返回地址、附加信息等信息。每个方法从调用到执行完成的过程,就对应着一个栈帧在虚拟机栈中的入栈(压栈)到出栈(弹栈)的过程。

Java 虚拟机栈使用的内存不需要保证是连续的。

Java 虚拟机规范即允许 Java 虚拟机栈被实现成固定大小(-Xss),也允许通过计算结果动态来扩容和收缩大小。如果采用固定大小的 Java 虚拟机栈,那每个线程的 Java 虚拟机栈容量可以在线程创建的时候就已经确定。

Java 虚拟机栈中的单位元素是栈帧,每个线程中调用同一个方法或者不同的方法,都会创建不同的栈帧。在 Running 的线程,只有当前栈帧有效(Java 虚拟机栈中栈顶的栈帧),与当前栈帧相关联的方法称为当前方法。每调用一个新的方法,被调用方法对应的栈帧就会被放到栈顶(入栈),也就是成为新的当前栈帧。当一个方法执行完成退出的时候,此方法对应的栈帧也相应销毁(出栈)。

每个栈帧中存放局部变量表、操作数栈、动态链接、方法返回地址、附加信息。

3.本地方法栈

本地方法栈(Native Method Stacks)与 Java 虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的 Native 方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。

Navtive 方法是 Java 通过 JNI 直接调用本地 C/C++ 库,可以认为是 Native 方法相当于 C/C++ 暴露给 Java 的一个接口,Java 通过调用这个接口从而调用到 C/C++ 方法。当线程调用 Java 方法时,虚拟机会创建一个栈帧并压入 Java 虚拟机栈。然而当它调用的是 native 方法时,虚拟机会保持 Java 虚拟机栈不变,也不会向 Java 虚拟机栈中压入新的栈帧,虚拟机只是简单地动态连接并直接调用指定的 native 方法。

4.堆(参考JVM系列-内存区域- Java堆(五))

Java 堆(Java Heap)是 Java 虚拟机所管理的内存中最大的一块,也被称为 "GC堆",是被所有线程共享的一块内存区域,在虚拟机启动时被创建。

唯一目的就是储存对象实例和数组(JDK7 已把字符串常量池和类静态变量移动到 Java 堆),几乎所有的对象实例都会存储在堆中分配。随着 JIT 编译器发展,逃逸分析、栈上分配、标量替换等优化技术导致并不是所有对象都会在堆上分配。

Java 堆是垃圾收集器管理的主要区域。堆内存分为新生代 (Young) 和老年代 (Old) ,新生代 (Young) 又被划分为三个区域:Eden、From Survivor、To Survivor。

根据 Java 虚拟机规范的规定,Java 堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样。在实现时,既可以实现成固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的(通过 -Xmx 和 -Xms 控制)。

5.方法区(https://www.jianshu.com/p/59f98076b382)

方法区(Method Area)与 Java 堆一样,是所有线程共享的内存区域。

JDK7 之前(永久代)用于存储已被虚拟机加载的类信息、常量、字符串常量、类静态变量、即时编译器编译后的代码等数据。

Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap(非堆),目的应该是与 Java 堆区分开来。

运行时常量池(Runtime Constant Pool)是方法区的一部分。Class 文件中除了有类的版本/字段/方法/接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将类在加载后进入方法区的运行时常量池中存放。运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的是 String.intern() 方法。

java 中基本类型的包装类的大部分都实现了常量池技术,这些类是 Byte、Short、Integer、Long、Character、Boolean,另外 Float 和 Double 类型的包装类则没有实现。另外 Byte、Short、Integer、Long、Character 这5种整型的包装类也只是在对应值在-128到127之间时才可使用对象池。

在老版jdk,方法区也被称为永久代(可以通过 -XX:PermSize 和 -XX:MaxPermSize 来进行调节大小),JDK8 彻底将永久代移除出 HotSpot JVM,将其原有的数据迁移至 Java Heap 或 Native Heap(Metaspace),取代它的是另一个内存区域被称为元空间(Metaspace)。

元空间(Metaspace):元空间是方法区的在 HotSpot JVM 中的实现,方法区主要用于存储类信息、常量池、方法数据、方法代码、符号引用等。元空间的本质和永久代类似,都是对 JVM 规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。元空间的大小理论上取决于32位/64位系统内存大小,可以通过 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 配置内存大小。

二、常说的java中的栈内存和堆内存

我们经常说的栈内存和堆内存只是java内存模型中的一部分内容,也就是编程过程中关注比较多的部分。

通常说的栈一般指栈帧中的局部变量表(存放的8种类型: byte、short、int、long、float、double、char、boolean和reference、returnAddress),它是一片连续的内存空间,用来存放方法参数,以及方法内定义的局部变量,存放着编译期间已知的数据类型。局部变量表所需要的内存空间在编译期完成分配,当进入一个方法时,这个方法在栈中需要分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表大小。

通常说的堆一般指java内存模型中的堆,用于储存对象实例和数组,几乎所有的对象实例都会存储在堆中分配。java堆是java虚拟机管理的内存中最大的一块,也被称为 "GC堆",是垃圾收集器管理的主要区域。

三、栈内存和堆内存的区别(只是便于记忆,并不严谨)

存储的数据及生命周期

栈主要用于存储方法参数、局部变量和对象的引用变量,存放的是编译期间已知的数据类型(八大基本类型和对象引用(reference类型),returnAddress类型。每个线程都会有一个独立的栈空间,栈内存的数据生命周期随线程的结束而结束。

所有对象实例及数组都要在堆上分配内存,堆存放的对象是线程共享的,线程结束时,对象实例和数组的生命周期并不一定结束,只有被GC回收后生命周期结束。

空间大小及限制

栈的内存大小在编译时确定,是一段连续的空间,运行时不会改变,栈内存随线程的结束自动回收。如果请求的栈的深度大于虚拟机允许的栈深度,JVM会抛出java.lang.StackOverFlowError。

堆内存在程序运行时动态分配,可以是存在物理上不连续的内存空间,线程运行结束后GC进行回收(只有对象或数组不再被引用时才回收)。如果是堆内存没有可用的空间存储生成的对象,JVM会抛出java.lang.OutOfMemoryError。

独占或共享

栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即栈内存可以理解成线程的私有内存。而堆内存中的对象对所有线程可见。堆内存中的对象可以被所有线程访问。

分配效率

栈由系统自动分配,速度较快。堆由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。

存取速度(jvm中可能会不同)

由于很多CPU对压栈、出栈操作有硬件(指令)上的支持,所以在栈区分配/归还内存速度极快(相比之下,堆上分配简直是龟速);尤其是函数内部的局部变量,可以轻易与函数调用/返回绑定,因此几乎所有编译型语言都会在利用栈管理局部变量(而且会优先使用空闲的寄存器,所以几乎所有高级语言都是访问局部变量速度最快)。

java中栈和堆都存哪些东西_java中栈内存与堆内存(JVM内存模型)相关推荐

  1. java接口那一节是哪的知识_Java中的接口知识汇总

    Java中的接口知识汇总 发布于 2020-4-29| 复制链接 本文给大家汇总介绍了在java中的接口知识,包括为什么要使用接口.什么是接口.抽象类和接口的区别.如何定义接口以及定义接口注意点,希望 ...

  2. [二]Java虚拟机 jvm内存结构 运行时数据内存 class文件与jvm内存结构的映射 jvm数据类型 虚拟机栈 方法区 堆 含义...

    前言简介 class文件是源代码经过编译后的一种平台中立的格式 里面包含了虚拟机运行所需要的所有信息,相当于 JVM的机器语言 JVM全称是Java Virtual Machine  ,既然是虚拟机, ...

  3. java中的成员变量和局部变量的区别_java中成员变量与局部变量区别分析

    本文实例分析了java中成员变量与局部变量区别.分享给大家供大家参考.具体分析如下: 成员变量:在这个类里定义的私有变量,属于这个类. 创建以及使用成员变量 public class Person { ...

  4. java 判断一个字符串是否由数字组成的_Java中怎样判断一个字符串是否是数字

    展开全部 1.使用Character.isDigit(char)判断 String str = "123abc"; if (!"".equals(str)) { ...

  5. java内存图怎么画,jvm内存模型怎么画?简单美观的模型图制作软件

    jvm内存模型是什么呢?jvm内存模型是一类用于描述由java栈.java堆.方法区.本地方法栈和程序计数器各部件构成的Java程序.在执行过程中,由jvm管理的不同数据区域的一类图表.jvm内存模型 ...

  6. JAVA面向对象OOP→构造方法、this、访问修饰符、static、方法重载、JVM内存分配、GC垃圾回收、包、样例代码

    构造方法 构造方法(constructor):与类名同名,没有返回值 其初始化作用:一为属性分配内存空间.二为属性赋值 构造方法不能自己调用,只能通过new实例化对象调用 隐式构造方法:对象实例化时编 ...

  7. java 查看内存占用_查看JVM内存使用状况

    1.jps:查看本地正在运行的java进程和进程ID(pid) 2.jinfo pid,查看指定pid的所有JVM信息 1)jinfo -flags pid 查询虚拟机运行参数信息. 2)jinfo ...

  8. java内存分配空间大小,JVM内存模型及内存分配过程

    一.JVM内存模型 JVM主要管理两种类型内存:堆(Heap)和非堆(Permanent区域). 1.Heap是运行时数据区域,所有类实例和数组的内存均从此处分配.Heap区分两大块,一块是 Youn ...

  9. Java所有函数都是动态的_Java中的函数动态调用

    //------------------------------------- //类ArgumentHolder //用于调用参数的封装,实现变长参数及 //不同类型参数的统一形式地传递 //成员变 ...

最新文章

  1. [EnterpriseServices]利用assembly定义我们的组件在COM+中的注册方式
  2. 基于JAVA的生产者消费者问题
  3. windbg查看SSDT表
  4. Android开发中如何创建自定义对话框
  5. python爬虫自动更换ip_Python 爬虫使用动态切换ip防止封杀
  6. 低脂肪肉能帮你减肥吗?
  7. Pixel Bender 之 Flex 特效使用
  8. 云计算是否为数据仓储做好了准备呢?
  9. 微信支付宝关闭三星Galaxy指纹支付;乐视网回应贾跃亭破产;Chrome 78发布|极客头条...
  10. 20.6. TeXstudio(LaTeX 编辑器)
  11. nginx的cgi模块
  12. 夜神模拟器连不上 adb 问题解决方法
  13. banner图片不拉伸、全屏宽、居中显示的方法
  14. Problem: 美丽的黄山 (指针)
  15. 赛尔号7月17日服务器维护,赛尔号07月17日更新攻略汇总 环城之光圣芒降临
  16. 概念模型计算机实验总结,计算机组成原理——cpu的简单模型实验报告
  17. 主机win10与虚拟机win7相互ping通方法
  18. 视频监控RTSP 客户端
  19. x86_64汇编之三:x86_64汇编和x86_32汇编的区别
  20. 简易封装 element form表单

热门文章

  1. uniapp中image不显示网络图片
  2. EML转PST转换器
  3. C语言学习笔记02-输入输出运算符
  4. 近似算法 近似比的粗浅了解和一些概念总结mark
  5. 红旗linux挂载硬盘命令,红旗linux 增加磁盘后linux认盘操作
  6. 中国老年医学学会:老年人预防新型冠状病毒感染的18条建议
  7. 服务器硬盘复制速度突然变慢,win10机械硬盘复制文件速度一开始快后来变慢怎么修复...
  8. iphone拍了照片相册里找不到
  9. CodeForces 148C-Terse princess
  10. IDEA 代码背景(护眼绿)