Java虚拟机详解(二)------运行时内存结构
首先通过一张图了解 Java程序的执行流程:
我们编写好的Java源代码程序,通过Java编译器javac编译成Java虚拟机识别的class文件(字节码文件),然后由 JVM 中的类加载器加载编译生成的字节码文件,加载完毕之后再由 JVM 执行引擎去执行。在加载完毕到执行过程中,JVM会将程序执行时用到的数据和相关信息存储在运行时数据区(Runtime Data Area),这块区域也就是我们常说的JVM内存结构,垃圾回收也是作用在该区域。
关于这幅图涉及到的:
①、class文件
②、类加载器
③、运行时数据区
④、执行引擎
⑤、垃圾回收器
这都是接下来将要介绍的重点。
本篇博客我们将首先介绍什么是运行时数据区。
PS:下面介绍的是根据 Java虚拟机规范 定义的运行时数据区,上一篇博客我们讲过根据虚拟机规范实现的虚拟机有很多个,而不同的虚拟机其运行时数据区定义也会有所不同。比如默认的 HotSpot 在实现 JDK1.7 虚拟机规范时,其常量池的定义不在方法区中,而是移到了堆中;到了 HotSpot JDK1.8 中,则彻底移除了持久代(方法区)而使用Metaspace(元数据区)来进行替代等等,关于这些区别本篇博客也会在文章末尾进行相应的说明。
1、运行时数据区结构图
①、Java虚拟机规范定义的运行时数据区
②、HotSpot JDK1.8定义的运行时数据区
注意:HotSpot实现的运行时数据区和Java虚拟机规范定义的还是有所不同的,
①、将Java虚拟机栈和本地方法栈合二为一;
②、元数据区取代了方法区,并且元数据区不在Java虚拟机中,而是在本地内存中。
③、运行时常量池由方法区中移到了堆中
2、程序计数器
程序计数器(Program Conputer Register)这是一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器,在虚拟机的概念模型里,字节码解释器的工作就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
①、线程私有
Java虚拟机支持多线程,是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任一确定的时刻,一个处理器只会执行一条线程中的指令,因此为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器。因此线程启动时,JVM 会为每个线程分配一个PC寄存器(Program Conter,也称程序计数器)。
②、记录当前字节码指令执行地址
如果当前线程执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是 Native 方法,则这个计数器值为空(Undefined)。
③、不抛 OutOfMemoryError 异常
程序计数器的空间大小不会随着程序执行而改变,始终只是保存一个 returnAdress 类型的数据或者一个与平台相关的本地指针的值。所以该区域是Java运行时内存区域中唯一一个Java虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域。
3、虚拟机栈
Java虚拟机栈(Java Virtual Machine stack),这块区域也是线程私有的,与线程同时创建,用于存储栈帧。Java 每个方法执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息,每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
①、线程私有
随线程创建而创建,声明周期和线程保持一致。
②、由栈帧组成
线程每个方法被执行的时候都会创建一个栈帧,用于存储局部变量表、操作栈、动态链接、方法出口等信息,每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
③、抛出 StackOverflowError 和 OutOfMemoryError 异常
如果线程请求的栈深度大于虚拟机所允许的深度,将抛出 StackOverflowError 异常;如果虚拟机栈可以动态扩展,当扩展时无法申请到足够的内存时会抛出 OutOfMemoryError 异常。
4、本地方法栈
本地方法栈(Native Method Stacks)作用和虚拟机栈类型,虚拟机栈执行的是Java方法,本地方法栈执行的是 Native 方法,本地方法栈也会抛出抛出 StackOverflowError 和 OutOfMemoryError 异常。
注意:由于虚拟机规范并没有对本地方法栈中的方法使用语言、使用方式和数据结构强制规定,因此具体的虚拟机可以自由实现它。上图我们也给出在 HotSpot 虚拟机中,本地方法栈和虚拟机栈合为一体了。
5、Java堆
Java堆是Java虚拟机所管理内存最大、被所有线程共享的一块区域,目的是用来存放对象,基本上所有的对象实例和数组都在堆上分配(不是绝对)。Java堆也是垃圾回收器管理的主要区域。
①、线程共享
堆存放的对象,某个线程修改了对象属性,另外一个线程从堆中获取的该对象是修改后的对象,为什么堆要设计成线程共享呢?
我们可以假设堆是线程私有的,很显然一个系统创建的对象会有很多,而且有些对象会比较大,如果设计成线程私有的,那么如果有很多线程同时工作,那么都必须给他们分配相应的私有内存,我相信内存很快就撑爆了,很显然将堆设计为线程共享是最好不过了,不过凡事都具有两面性,线程共享的设计这也带来了多线程并发资源冲突问题,关于这个问题由于不是本系列博客的主旨,这里就不做详细介绍了。
②、存放对象
基本上所有的对象实例和数组都要在堆上进行分配,但是随着 JIT 编译器的发展和逃逸分析技术的成熟,栈上分配、标量替换等优化技术会导致对象不一定在堆上进行分配。
③、垃圾收集
Java堆也被称为“GC堆”,是垃圾回收器的主要操作内存区域。当前垃圾回收器都是使用的分代收集算法,所以Java堆还可以分为:新生代和老年代,而新生代又可以分为 Eden 空间、From Survivor 空间、To Survivor空间。这是为了更好的回收内存,关于垃圾回收算法在后续博客会详细介绍。
④、抛出 OutOfMemoryError 异常
根据Java虚拟机规范,Java堆可以处于物理上不连续的内存空间中,只要逻辑上连续即可,实现时既可以实现成固定大小,也可以是扩展的。如果在堆中没有完成实例分配,并且堆也无法扩展,将抛出OutOfMemoryError 异常。
6、方法区
方法区(Method Area)用来存储已被Java虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
方法区也称为“永久代”,这是因为垃圾回收器对方法区的垃圾回收比较少,主要是针对常量池的回收以及对类型的卸载,回收条件比较苛刻。经常会导致对此内存未完全回收而导致内存泄露,最后当方法区无法满足内存分配时,将抛出 OutOfMemoryError 异常。
PS:在Java虚拟机规范中把方法区描述为堆的一个逻辑部分(https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5.4),在很多虚拟机中(JRockit、IBM J9等虚拟机不存在永久代的概念)。
在JDK1.8 的 HotSpot 虚拟机中,已经去掉了方法区的概念,用 Metaspace 代替,并且将其移到了本地内存来规划了。
7、运行时常量池
在Java虚拟机规范中,运行时常量池(Runtime Constant Pool)用于存放编译期生成的各种字面量和符号引用,是方法区的一部分。但是Java虚拟机规范对其没有做任何细节的要求,所以不同虚拟机实现商可以按照自己的需求来实现该区域,比如在 HotSpot 虚拟机实现中,就将运行时常量池移到了堆中。
①、存放字面量、符号引用、直接引用
通常来说,该区域除了保存Class文件中描述的引用外,还会把翻译出来的直接引用也存储在运行时常量池,并且Java语言并不要求常量一定只能在编译器产生,运行期间也可能将常量放入池中,比如String类的intern()方法,当调用intern方法时,如果池中已经包含一个与该String
确定的字符串相同equals(Object)
的字符串,则返回该字符串。否则,将此String
对象添加到池中,并返回此对象的引用。关于该方法的介绍可以看我这篇博客。
②、抛出 OutOfMemoryError 异常
运行时常量池是方法区的一部分,会受到方法区内存的限制,当常量池无法申请到内存时,会抛出该异常。
8、直接内存
直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,它也不是Java虚拟机规范定义的内存区域。我们可以看到在 HotSpot 中,就将方法区移除了,用元数据区来代替,并且将元数据区从虚拟机运行时数据区移除了,转到了本地内存中,也就是说这块区域是受本机物理内存的限制,当申请的内存超过了本机物理内存,才会抛出 OutOfMemoryError 异常。
直接内存也是受本机物理内存的限制,在JDK1.4中新加入的 NIO(new input/output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的 I/O 方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在Java堆里面的 DirectByteBuffer 对象作为这块内存的引用操作,这样避免了在Java堆和Native堆中来回复制数据,显著提高性能。
Java虚拟机详解(二)------运行时内存结构的更多相关文章
- Java虚拟机详解(六)------内存分配
我们说Java是自动进行内存管理的,所谓自动化就是,不需要程序员操心,Java会自动进行内存分配和内存回收这两方面. 前面我们介绍过如何通过垃圾回收器来回收内存,那么本篇博客我们来聊聊如何进行分配内存 ...
- Java虚拟机详解----JVM常见问题总结
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
- Java虚拟机详解(三)------垃圾回收
如果对C++这门语言熟悉的人,再来看Java,就会发现这两者对垃圾(内存)回收的策略有很大的不同. C++:垃圾回收很重要,我们必须要自己来回收!!! Java:垃圾回收很重要,我们必须交给系统来帮我 ...
- Java虚拟机详解(十)------类加载过程
在上一篇文章中,我们详细的介绍了Java类文件结构,那么这些Class文件是如何被加载到内存,由虚拟机来直接使用的呢?这就是本篇博客将要介绍的——类加载过程. 1.类的生命周期 类从被加载到虚拟机内存 ...
- Mysql常用show命令,show variables like xxx 详解,mysql运行时参数
MySQL中有很多的基本命令,show命令也是其中之一,在很多使用者中对show命令的使用还容易产生混淆,本文汇集了show命令的众多用法. 详细: http://dev.mysql.com/doc/ ...
- Java 虚拟机详解
深入理解JVM 1 Java技术与Java虚拟机 说起Java,人们首先想到的是Java编程语言,然而事实上,Java是一种技术,它由四方面组成: Java编程语言.Java类文件格式.Java虚 ...
- Java虚拟机详解(一)------简介
本系列博客我们将以当前默认的主流虚拟机HotSpot 为例,详细介绍 Java虚拟机.以 JDK1.7 为主,同时介绍与 JDK1.8 的不同之处,通过Oracle官网以及各种文献进行整理,并加以验证 ...
- Java-JVM 运行时内存结构(Run-Time Data Areas)
Java 虚拟机定义了在程序执行期间使用的各种运行时数据区域. 其中一些数据区域所有线程共享,在 Java 虚拟机(JVM)启动时创建,仅在 Java 虚拟机退出时销毁. 还有一些数据区域是每个线程的 ...
- JVM 运行时内存结构
1.JVM内存模型 JVM运行时内存=共享内存区+线程内存区 1).共享内存区 共享内存区=持久带+堆 持久带=方法区+其他 堆=Old Space ...
随机推荐
- 用ORACHK自己主动化检查数据库系统的健壮性
1.orachk工具主要用途 (1)主动检查您的整个软件在操作系统.CRS.数据库.高可用等层面中的严重问题,以便于IT部门整改,提升系统的稳定性 (2)对于您系统中存在的风险提供简单化和合理化的诊断 ...
- Android sendToTarget
在使用message进行handler的数据交互的时候不可避免的会使用到message作为数据的载体,可是在使用message的时候有人会直接new一个message,有人会使用handler.obt ...
- aspnetboilerplate && .net core 使用原生sql
利用aspnetboilerplate提供的工具类IDbContextProvider private readonly IDbContextProvider<XXXDbContext> ...
- debian 下py2.7 安装mysql模块
先安装pip 然后用pip安装 setuptools 安装模块的时候会报错 python setup.py install sh: mysql_config: command not found Tr ...
- 你好,Oh My Zsh - 社区力量全新方式定义命令行 | 咖啡时间
Oh My Zsh 是一款社区驱动的命令行工具,正如它的主页上说的,Oh My Zsh 是一种生活方式.它基于 zsh 命令行,提供了主题配置,插件机制,已经内置的便捷操作.给我们一种全新的方式使用命 ...
- dumpbin判断windows程序是32还是64位(包括DLL)
http://blog.csdn.net/csfreebird/article/details/10105681 dumpbin /HEADERS gdal18.dll(or xxx.exe) 如果安 ...
- WPF依赖属性对内存的使用方式
WPF允许对象在创建时候并不包含存储数据的空间,只保留在用到时获取数据默认值,借用其他对象数据或者实时分配空间的能力
- WPF控件的一些特殊应用
1 checkbox.IsChecked 返回的是bool?类型,需要用bool强转,或者直接和bool类型比较,将发生隐形转换 2 RadioButton有分组属性GroupName
- Word Embeddings: Encoding Lexical Semantics
Word Embeddings: Encoding Lexical Semantics Getting Dense Word Embeddings Word Embeddings in Pytorch ...
- Windows Phone 8.1之应用设置存储的简单应用
应用设置存储主要分为本地存储和漫游存储两种方式.本地存储的指将应用的设置信息存储在本地存储空间中,而漫游存储则是指将应用的设置信息存储在网络服务器中.相对于本地存储而言,漫游存储支持多台设备之间的应用 ...