一、前言

??之前过年在家,空闲时间比较多,想要了解一下JVM相关的内容,于是买了《深入理解Java虚拟机》这本书,过了一遍其中的基础知识。时隔多日,都忘得差不多了。为了重新捡起来,我决定复习一遍,并编写相关的系类博文加深印象,这是第一篇,来讲一讲JVM最基础的内容——Java的内存模型。

二、正文

?2.1 Java内存分布

??Java的内存主要分为五个部分:

程序计数器;

Java虚拟机栈;

本地方法栈;

堆内存;

方法区;

??具体结构如下图所示:

2.2 程序计数器

??首先看第一部分——程序计数器。上过计算机组成原理的可能都听过这个名词,也大概知道它的作用是指向下一条需要执行的计算机指令,而在JVM中,程序计数器的作用也是类似。

??程序计数器是JVM中较小的一块内存空间,它的作用是记录下一条需要执行的字节码指令的地址(Java代码编译后的字节码在未经过实时编译器编译前,其执行方式是通过“字节码解释器”进行解释执行)。在JVM的概念模型里,就是通过改变这个计数器的值来辅助程序的执行。选择,循环,方法调用,异常处理等功能都需要通过程序计数器的辅助来完成。

??JVM的多线程和CPU的多线程处理类似,都是使用时间片轮转的方式来执行各个线程,即每个线程轮流得到一个时间片,执行完这个时间片后,等待下一次调度,而每次只有一个线程在执行。正因为如此,线程在调度后,为了能够恢复到这个线程上一次执行的位置,就需要程序计数器的帮助,让程序计数器来告知下一条需要执行的指令的地址,对于每一个线程都是如此。所以,JVM中的程序计数器是线程私有的,即每一个线程都有一个独立的程序计数器,辅助当前线程的运行,各个线程之间互不影响。

??如果当前JVM执行的是一个Java方法,则程序计数器的值就是正在执行的字节码指令的地址;若当前执行的是一个本地方法(如Object的hashcode方法),则程序计数器的值为空(Undefined),因为本地方法并不会被加载到JVM的内存空间中。

?2.3 Java虚拟机栈

??虚拟机栈和程序计数器一样,也是线程私有的。Java虚拟栈是Java方法执行时需要依赖的内存空间,这个栈中的元素被称为栈帧。栈帧可以理解为一个对象,它记录了一个方法的局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法对应一个栈帧,而每一个方法从执行到返回,就对应着它所属的栈帧从入栈到出栈的过程,即当一个Java方法被调用,JVM就会创建这个方法的栈帧,并将它加入的虚拟机栈中,执行结束后才会出栈。对于JVM来说,正在执行的是虚拟机栈栈顶的栈帧所对应的方法,这个栈帧被称为当前栈帧,栈帧对应的方法被称为当前方法。

??下面我来简单介绍一下栈帧最重要的两个部分:

局部变量表:局部变量表是用来存放方法的参数以及在方法中定义的局部变量,它的大小在编译期间就被确定,并写入到class文件中。若一个变量是基本数据类型,则在局部变量表中存放的就是这个变量的具体值;若是引用类型,存放的则是这个变量所引用对象的地址。局部变量表的最小单位是变量槽(Slot),对于double和long类型的变量,在局部变量中需要占用两个单位,其他类型只需要占用一个单位。

操作数栈:从名字可以看出,这是一个栈Stack,它存放的是:方法运行时需要用到的操作数。和局部变量表一样,它的最大深度也是在编译期间就被计算好,并被写入class文件中的。在一个方法刚准备运行前,它是空的,而在运行的过程中,不断的有操作数被入栈和出栈(比如假设方法中需要计算num = 1 + 2,则在这条语句执行时,1和2被入栈,然后被取出,计算出3存入栈中,再将3取出赋值给num),程序的计算都需要依靠这个栈进行。操作数栈可以存放任意类型的数据(基本数据类型和引用类型,引用类型存放的是地址),而double和long类型将占用两个单位,其余类型占用一个单位。

??在虚拟机规范中规定,若线程的虚拟机栈的深度超过了JVM允许的最大深度,将抛出StackOverflowError(比如调用没有出口的递归方法);若JVM实现的虚拟机栈可以动态扩展,则如果在扩展时没有申请到足够的内存,将抛出OutOfMemoryError异常。

?2.4 本地方法栈

??本地方法栈的作用和虚拟机栈相同,不同的是,虚拟机栈是辅助Java方法的运行,而本地方法栈是辅助本地方法的运行。在虚拟机规范中,并没有规定实现本地方法栈使用的语言,数据结构以及使用方式,所以JVM可以自由地实现它。与虚拟机栈一样,这一块内存区域也可能抛出StackOverflowError和OutOfMemoryError异常,同时也是线程私有。

?2.5 堆内存

??这一块内存是JVM中最大,可能也是最重要的一块内存,而堆存在的唯一目的就是存放实例对象,几乎所有的对象都是在堆中分配内存。和之前所说的几块内存不同,堆内存是所有的线程共享的,它在虚拟机启动时被创建。

??堆内存是垃圾回收的主要区域,从内存回收的角度看,现在的垃圾回收机制一般都是分代收集,所以堆内存可以被分为新生代和老年代。新生代用来存放生命周期短的对象,这一块区域将频繁进行垃圾回收;老年代用来存放生命周期长的对象(或者在新生代内存不足时代替新生代存放对象),所以垃圾回收的频率较低,甚至不进行垃圾回收。

??根据Java虚拟机规范规定,堆可以是不连续的内存空间,只需要逻辑上连续即可。在实现虚拟机时,可以将堆实现为固定大小,也可以实现为可扩展的,当前的虚拟机一般都是采用可扩展的方式(可以通过配置参数来改变堆的大小)。如果堆中的内存被占满,实例对象无法存入,且堆无法再进行扩展时,将抛出OutOfMemaryError异常。

?2.6 方法区

??和堆一样,方法区也是线程共享的,它的作用是存放类信息,常量,静态常量等内容。Java虚拟机规范对这一块区域的限制非常宽松,方法区可以选择实现为固定的或者可扩展的,也可以选择是否实现垃圾回收。方法区中的数据,很少会发生改变,所以虚拟机规范并不强制要求在此区域实现垃圾回收。这块区域进行垃圾回收的主要目的就是卸载已经不需要使用的类,但是类卸载的条件是非常苛刻的。

??在方法区中有一块内存区域,叫做运行时常量池,它的作用就是存储编译期字面量以及符号引用。在一个类中如果定义了常量,或使用了字面量(如直接使用字符串为变量赋值,String str = ”aaa“),则在编译之后,会被放入到class文件的常量池中(class文件有一个常量池部分),同时,类编译后的符号引用也会被放入常量池。当程序运行时,class中的内容被加载到方法区中,而其中常量池的内容会被放入运行时常量池里面,被所有的类共享。运行时常量池是一个动态的内存区域,可以在程序运行时,动态的为其中添加数据,比如String类的intern()方法。因为运行时常量池是方法区的一部分,所以它的大小受到方法区的限制,当无法在其中申请空间时,将抛出OutOfMemaryError异常。

?2.7 我们平常理解的Java内存模型

??我们在学习Java的过程中,经常会看到这样的内容:Java的内存分为两部分,即栈内存和堆内存,栈内存中存放基本数据类型以及对象引用,而堆内存中存放对象。

??以上是对Java内存模型的片面描述,只关注了程序员最重视的部分。在上面的描述中,栈内存指的就是本地方法栈中,局部变量表的部分;而堆内存就是Java中的堆了。

三、总结

??以上内容对Java的内存模型进行了一个简单的描述,讨论了每一个部分的基本作用,了解了上面的内容后,再遇到Java内存方面的问题,也能更好地找出错误的所在。

四、参考

《深入理解Java虚拟机》

原文:https://www.cnblogs.com/tuyang1129/p/12499181.html

JAVA引用类型在变量槽,浅析Java的内存模型相关推荐

  1. 头歌(educoder)第 1 章 Java入门之变量与表达式 Java入门 - 运算符和表达式

    educoder(头歌)第 1 章 Java入门之变量与表达式 Java入门 - 运算符和表达式 第1关:算数运算符 package step1; import java.util.Scanner;p ...

  2. Java虚拟机学习(1):体系结构 内存模型

    一:Java技术体系模块图 二:JVM内存区域模型 1.方法区 也称"永久代" ."非堆",  它用于存储虚拟机加载的类信息.常量.静态变量.是各个线程共享的内 ...

  3. java引用类型和值类型_[Java教程]JavaScript中值类型和引用类型的区别

    [Java教程]JavaScript中值类型和引用类型的区别 0 2017-02-24 00:00:35 JavaScript的数据类型分为两类:原始类型和对象类型.其中,原始类型包括:数字.字符串和 ...

  4. java 接口中变量修饰符,Java的访问修饰符与变量的作用域讲解

    Java访问修饰符(访问控制符) Java 通过修饰符来控制类.属性和方法的访问权限和其他功能,通常放在语句的最前端.例如: ? Java 的修饰符很多,分为访问修饰符和非访问修饰符.本节仅介绍访问修 ...

  5. java 易变变量_关于java:易变变量和其他变量

    以下是经典Concurency in Practice的内容: When thread A writes to a volatile variable and subsequently thread ...

  6. Java基础-环境变量设置及Java命令行使用

    2019独角兽企业重金招聘Python工程师标准>>> 一.Java环境变量设置 windows 下配置JDK环境变量: 1. 安装JDK,安装过程中可以自定义安装目录等信息,例如我 ...

  7. java实现linux变量替换_linux java 配置 含环境变量 | 学步园

    一般情况下,我们都要将linux自带的OPENJDK卸载掉,然后安装SUN的JDK. 首先查看Linux自带的JDK是否已安装. 输入如下命令,查看已经安装的JAVA版本信息. Linux代码   j ...

  8. Java配置环境变量教程,Java配置环境变量的作用

    配置Java环境变量的作用 1:可以在cmd窗口(dos命令)运行和编译Java代码, 2:在任何路径下都能运行jdk,不配置环境变量的话,只能在jdk的安装路径下运行,换到其他路径的 话系统会提示找 ...

  9. java设置系统环境变量_设置java 环境变量

    DOS下任意目录用JAVA,JAVAC肯定是显示正确咯,因为你设置好了JAVAlib和bin的但是JAVA文件需要DOS下CD好了目录才能JAVAC否则是在C:\program里查找该JAVA文件你任 ...

最新文章

  1. OCA读书笔记(9) - 管理数据同步
  2. 汤家凤高等数学基础手写笔记-空间解析几何
  3. allegro PCB 引脚网络名不显示之解决办法
  4. 如何基于Restful ABAP Programming模型开发并部署一个支持增删改查的Fiori应用
  5. 数据库存取BLOB类型音乐文件的过程及常见错误
  6. redis 远程主机强迫关闭了一个现有的连接_如何在 Debian 10 上安装和配置 Redis 服务...
  7. WF从入门到精通(第十章):事件活动 (转)
  8. c ++结构体构造函数_C ++中的构造函数
  9. 《Predicting Loose-Fitting Garment Deformations Using Bone-Driven Motion Networks》Reading Notes
  10. OI队的土豆树(C++)
  11. 操作系统权限提升(十五)之绕过UAC提权-基于白名单DLL劫持绕过UAC提权
  12. iar stm32_STM32强大的生态,在这里一起总结!
  13. Linux系统如何更新升级
  14. OpenCV钢铁平面焊接的缺陷检测案例
  15. 女神节活动 送她链上专属藏品,爱要大胆晒出来
  16. BT TWS方案开发感悟
  17. 使用html5+javascript+css实现单机版五子棋---跟电脑打没赢过
  18. w ndows7与windows10区别,电脑系统windows 7和windows 10有什么区别?哪个更好?
  19. 人工智能发展文献检索报告
  20. 转载的:flash 中文不显示问题

热门文章

  1. Vue2.0 + ElementUI 手写权限管理系统后台模板(三)——页面搭建
  2. 《HFSS电磁仿真设计从入门到精通》一2.3 T形波导的优化分析
  3. 解决gvim中php函数提示php_funclist.dict无法生效的问题
  4. 好程序员训练营-Java变量的作用域
  5. 完美解决IE6中fixed抖动问题的方法
  6. 最优秀的5个Linux文本编辑器
  7. Android Activity中重写onCreateOptionsMenu不显示菜单按钮
  8. spring图片转视频_视频转GIF在线教程,GIF制作方法
  9. php聊天室禁止提交,phpcms v9禁止提交信息到官网方法详解
  10. Zookeeper之Watcher机制详解