java虚拟机在执行java程序的时候会把它所管理的内存分为多个不同的区域,每个区域都有不同的作用,以及由各自的生命周期,有些随着虚拟机进行的启动而存在,有些区域则依赖于用户线程的启动或结束而建立或销毁等。在《java虚拟机规范(Java SE7版)》中规定,java内存分为以下一种,如图所示:

  1、程序计数器

  程序计数器(Program Counter Register)是一个内存较小的区域,它可以被看作是当前线程所执行到的字节码的行号的指示器,字节码解释器在执行下一条指令,比如分支,跳转,循环,异常处理等都需要依赖这个计数器来完成的。

  由于java多线程是通过不断的切换分配处理器时间片的方式来实现的,在任何一个确定的时刻,一个处理器都只会执行一条指令。也就是说,在多线程中每个线程都可以抢到cpu的时间片,那么别抢去的线程会立即停止下来,直到它再一次获得cpu的时间片。那么,java虚拟机是如何确保再一次获得处理器时间片的时候能够在正确的位置上继续执行指令?java虚拟机就是通过当前线程的程序计数器保证的,因为保存了当前线程上次执行结束的位置,因此,程序计数器是每个线程独有的,各线程之前的程序计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

  如果线程正在执行java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址,如果正在执行的是native方法,那么这个计数器值则为空(Undefined);

  因为程序计数器记录的是当前线程字节码执行指令的地址,所以它的内存大小是不会随着线程的执行而发生变化,因此,该内存区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemeryError(内存溢出)的情况的区域。

  2.java虚拟机栈

  Java虚拟机栈与程序计数器一样都是线程私有的,它的生命周期与线程一致,每当创建一个新的线程时都会产生一个新的的虚拟机栈,线程销毁时虚拟机栈也随即销毁。每个方法的执行都会创建一个栈帧用于存储局部变量、操作数栈、动态链表,方法的出口等信息。每个方法的执行到结束就对应着一个栈帧在虚拟机中入栈和出栈的过程。

  我们在日常开发的过程的中经常会把java内存分为堆内存和栈内存,这个说法是不准确的,因为java内存区域的划分远比这个复杂的多,这种划分的方法只能说明程序员最关注的,与对象内存分配关系最密切的内存区域这两块,这里所说的栈其实指的是虚拟机中的局部变量表。局部变量表中存放了编译期可知的基本数据类型(char,byte,int,boolean,short,float,long,double)、对象的引用(reference类型,指的是对象地址的引用指针或是句柄)、returnAddress类型(指向字节码指令的地址)。

  局部变量表所需的内存空间在编译期完成分配,当进入一个方法时,这个方法所需要的栈帧的大小就已经确定了,在运行期间不会改变局部变量表的大小。其中64位长度的long和double类型的数据会占用两个局部变量空间(Slot),其余的数据类型只能占用1个。

  在java虚拟机规范中,对虚拟机栈定义了两种异常:第一,如果线程请求的深度大于虚拟机栈所允许的最大深度,将抛出StackoverflowError异常,第二,如果虚拟机栈可以动态的扩展,当扩展时无法申请到足够的内存,则会抛出OutOfMemoryError异常。

  2.1、运行时栈帧

  栈帧是Java虚拟机用来进行方法的调用和方法的指定的数据结构,它是虚拟机运行时数据库的Java虚拟机栈的栈元素。每一个栈帧都包含局部变量表、操作数栈、动态链接、方法返回地址和一些额外的附加信息。

  局部变量:方法的参数和方法中声明的局部变量都存储在局部变量中。在Class编译的时候就已经确定了的局部变量表的最大容量,声明在方法的Code属性的max_locals数据项中。变量槽(Slot)是局部变量表的最小单位,可以存储32位和64位的数据,64位的数据则需要两个连续的变量槽来表示。

  操作数栈:也成为操作栈,是一个后进先出的结构。方法执行过程中的算术运算或者调用其它方法的参数传递的时候都是在操作数栈中进行的。

  动态链接:Class 文件中存放了大量的符号引用,字节码中的方法调用指令就是以常量池中指向方法的符号引用作为参数。这些符号引用一部分会类第一次加载或第一次引用的时候转化为直接引用,这种转化称为静态解析。另一部分将在每一次运行期转化为直接引用,这部分称为动态连接。

  返回地址:当一个方法执行完成后的出口,有两个情况:一是正常情况退出,会将返回值传递给上一个方法的调用者,另一种是异常情况,此时是没有返回值的。

  3.本地方法栈

  本地方法栈和java虚拟机栈的作用是差不多的,他们的唯一区别在于:java虚拟机栈为虚拟机执行java方法(字节码)服务,而本地方法栈则为虚拟机中Native方法服务。在虚拟机规范中并没有明确的规定本地方法栈使用何种语言与数据结构,可有具体的虚拟机去实现它。本地方法栈和java虚拟机栈一样,也会抛出StackoverflowError和OutOfMemoryError两个异常。

  4.java堆

  java堆是java内存管理区域中最大的一块,java堆是所有线程共享的一块内存区域,在虚拟机启动时创建。java堆是用来存放对象,几乎所有的java对象和数组都存放在java队中,都在堆中分配空间。

  java堆是立即回收器管理的主要区域,因此有人也会把java堆称为“GC堆”。从垃圾回收的角度来看,由于现在大多数的收集器都是采用分代收集算法,所以java堆还可以细分为:新生代和老年代;再细致一点有可以分为Eden空间,From Survivor空间、To Survivor空间等;从内存分配的角度来看,线程共享的java堆中可能划分为多个线程私有的分配缓冲区(TLAB)。这样分配是为了更好的回收内存和创建内存等,与存放的区域无关,都是存放java对象。

  根据java虚拟机规范的规定,java堆可以是物理上不连续的内存空间,只要逻辑上连续就可以了。在实现时可以设置固定大小的空,也可以动态扩展的,不过当前流程的虚拟机都是可以动态来扩展的。如果在堆中没有足够的空间来分配实例对象,或者无法扩展堆空间,那么就会抛出OutOfMemoryError异常。

  5.方法区

  方法区和java堆一样,都是线程共享的内存区域,它主要用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。虽然java虚拟机规范把方法区描述为java堆的一部分,但是它却有一个别名叫做Non-Heap(非堆),目的是与java堆分开。

  java虚拟机规范对方法区的限制非常的宽松,除了和java堆一样不需要物理上连续的内存和可以选择固定大小空间或动态扩展为,还可以选择不实现垃圾回收。相对而言,垃圾回收是比较少在这个区域中出现的,但并非进入到方法区的数据都能永久的存在的。这块区域的的内存回收目标主要是针对常量池的回收和对类型的卸载,但是回收的效果并不是非常明显,特别是对类型的卸载,条件非常的苛刻,但这块内存的回收是非常必要的。在SUN公司的BUG列表中,曾经出现过若干个非常严重的BUG,就是由于低版本中Hotspot虚拟机没有对此内存区域进行回收造成的。

  根据java虚拟机规范的规定,当方法区无法满足内存分配的需求时,将抛出OutOfMemoryError异常。

  6.运行时常量池

  运行时常量池是属于方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,这部分的内容将在类加载完成 后进入到方法区的运行时常量池中存放。

  运行时常量池相对于Class文件常量池的另一个特征是具备动态性,java语言并不要求常量一定只有编译期才能产生,也就是并非预置入Class文件中的常量池的内容才可以进入方法区的运行时常量池,运行期间也可以将新的常量放入到池中,比如String类的intern方法。

  既然运行时常量池是存在于方法区中的,那么在无法分配到足够的内存是也会抛出OutOfMemoryError异常。

  

  参考书籍: 《深入理解Java虚拟机》

  欢迎大家关注公众号: 【java解忧杂货铺】,里面会不定时发布一些技术博客;关注即可免费领取大量最新,最流行的技术教学视频:

深入理解java虚拟机之java内存区域的更多相关文章

  1. Java虚拟机运行时内存区域简析

    figure:first-child { margin-top: -20px; } #write ol, #write ul { position: relative; } img { max-wid ...

  2. java虚拟机和java内存区域概述

    什么是虚拟机,什么是Java虚拟机 虚拟机 定义:模拟某种计算机体系结构,执行特定指令集的软件 系统虚拟机(Virtual Box.VMware),进程虚拟机 进程虚拟机 jvm.Adobe Flas ...

  3. 深入理解Java虚拟机之Java内存区域与内存溢出异常

    Java内存区域与内存溢出异常 运行时数据区域 程序计数器 用于记录从内存执行的下一条指令的地址,线程私有的一小块内存,也是唯一不会报出OOM异常的区域 Java虚拟机栈 Java虚拟机栈(Java ...

  4. 深入理解Java虚拟机之Java内存区域随笔

    1.java内存区域与内存溢出异常 Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域:1.程序计数器,2.栈(虚拟机栈和本地方法栈 ),3.堆,4.方法区(包含 ...

  5. 深入理解Java虚拟机(1)--Java内存区域

    运行时数据区域 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则是依赖用 ...

  6. java虚拟机学习-JVM内存管理:深入Java内存区域与OOM(3)

    概述 Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又 ...

  7. 深入理解Java虚拟机(自动内存管理机制)

    文章首发于公众号:BaronTalk 书籍真的是常读常新,古人说「书读百遍其义自见」还是很有道理的.周志明老师的这本<深入理解 Java 虚拟机>我细读了不下三遍,每一次阅读都有新的收获, ...

  8. 深入理解Java虚拟机之JVM内存布局篇

    内存布局**** ​ JVM内存布局规定了Java在运行过程中内存申请.分配.管理的策略,保证了JVM的稳定高效运行.不同的JVM对于内存的划分方式和管理机制存在部分差异.结合JVM虚拟机规范,一起来 ...

  9. 深入理解java虚拟机【Java内存结构】

    Java虚拟机规范规定的java虚拟机内存其实就是java虚拟机运行时数据区,其架构如下: 其中方法区和堆是由所有线程共享的数据区. Java虚拟机栈,本地方法栈和程序计数器是线程隔离的数据区. (1 ...

随机推荐

  1. 零基础自学Python十天,写了一款猜数字小游戏,附源码和软件下载链接!

    自学一门语言最重要的是要及时给自己反馈,那么经常写一些小程序培养语感很重要,写完可以总结一下程序中运用到了哪些零散的知识点. 本程序中运用到的知识点有: 1.输入输出函数 (input.print) ...

  2. [转]FFMpeg框架代码阅读

    简介 FFmpeg是一个集录制.转换.音/视频编码解码功能为一体的完整的开源解决方案. FFmpeg的开发是基于Linux操作系统,但是可以在大多数操作系统中编译和使用.FFmpeg支持MPEG.Di ...

  3. JavaScript函数节流和函数防抖之间的区别

    一.概念解释  函数节流和函数防抖,两者都是优化高频率执行js代码的一种手段.  大家大概都知道旧款电视机的工作原理,就是一行行得扫描出色彩到屏幕上,然后组成一张张图片.由于肉眼只能分辨出一定频率的变 ...

  4. css初始化标签属性--源码

    body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, fieldset, input, p, blockquote ...

  5. ImportError: numpy.core.multiarray failed to import

    1. ImportError: numpy.core.multiarray failed to import pip install -U numpy http://stackoverflow.com ...

  6. python_方法说明

    方法用来描述对象所具有的行为,例如,列表对象的追加元素.插入元素.删除原宿.排序,字符串对象的分隔.连接.排版.替换.烤箱的温度设置.烘烤,等等 在类中定义的方法可以粗略分为四大类:公有方法.私有方法 ...

  7. React从入门到放弃之前奏(1):webpack4简介

    接触webpack是好久之前的事情了,最近看了下webpack没想到都到4了. webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler). 会创建1个 ...

  8. adSage :2013年教育行业搜索引擎投放分析报告

  9. Windows上设置Mozilla Thunderbird邮件客户端后台运行

    作者:荒原之梦 操作系统: Windows 10 Thunderbird版本: 52.6.0(32-bit) Thunderbird官网页面:https://www.mozilla.org/zh-CN ...

  10. 什么是设计思维Design Thinking——风靡全球的创造力培养方法

    “把学习带到现实中,让孩子用自己的力量创造改变,可以直接提升他们的幸福感和竞争力.” 这是“全球孩童创意行动”的发起人——Kiran Sethi在TED演讲时说的一句话,这个行动旨在引导中小学生主动寻 ...