写在前面
本文描述的有关于 JVM 的运行时数据区是基于 HotSpot 虚拟机。
概述
JVM 在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机的进程启动而存在,有的区域则依赖于用户线程的启动和结束而建立和销毁。
HotSpot 运行时数据区
运行时数据区在 HotSpot 1.8 之前的版本和 1.8 版本有所不同,主要是 方法区移到元空间 了。
图 1-1:JDK1.8 之前 JVM 运行时数据区
图 1-2:JDK1.8 JVM 运行时数据区
线程私有区域
程序计数器(PROGRAM COUNTER REGISTER)
程序计数器是一块很小的区域,它存储的是当前线程正在执行的字节码的地址(在这里,其实有两个“当前”,一个是:当前正在被 CPU 执行的线程,另一个是:当前这个被执行的线程中正在被执行的字节码指令)。字节码解释器工作时就是改变程序计数器的值来选取下一条需要执行的字节码。对于单核心而言,多线程是通过线程轮流切换的方式实现的,在任一时刻只有一个线程能够得到 CPU 的执行权从而执行线程中的字节码指令,因此,为了使线程切换后能够恢复到正在执行的字节码的位置,每个线程都需要拥有自己的程序计数器。
注意:程序计数器是唯一的一块在 Java 虚拟机规范中没有规定任何 OutOfMemoryError 的区域。由于它是线程私有的,所以它的生命周期随着线程的创建而创建,随着线程的结束而死亡 。
虚拟机栈(VM STACK)
虚拟机栈也是线程私有的,所以它的生命周期与程序计数器相同。虚拟机栈描述的是 Java 方法执行的内存模型。
每个方法在执行的时候都会创建一个栈帧(一个方法对应一个栈帧,栈帧即栈的基本单位)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法被线程执行从开始到结束,就对应着一个栈帧在虚拟机栈中入栈(压栈)和出栈(弹栈)的过程。局部变量表中存放了编译可知的各种基本数据类型(byte,short,int,long,float,double,char,boolean)、对象引用(reference 类型,它存储的是:对象的地址或者是指向代表对象的句柄)。
Java 虚拟机规范中规定了虚拟机栈可能出现的两种异常状况:*Error 和 OutOfMemoryError。
*Error: 若当线程请求栈的深度超过当前 Java 虚拟机栈的最大深度的时候会抛出 *Error。
OutOfMemoryError: 若虚拟机栈动态扩展过程中,如果线程请求申请栈空间无法申请到足够的内存,就会抛出 OutOfMemoryError。
本地方法栈(NATIVE METHOD STACK)
本地方法栈与虚拟机栈类似,虚拟机栈是执行 Java 方法开辟的内存空间,而本地方法栈是执行 Native 方法开辟的内存空间。
与虚拟机栈一样,本地方法栈也会抛出 *Error 和 OutOfMemoryError 异常,抛出条件也是类似的。
线程间共享的内存区域
堆(HEAP)
堆是所有线程共享的一块区域,主要用来存放对象和数组。
在 Java 虚拟机规范中有描述:所有的对象实例和数组都要在堆上分配,但是 随着 JIT(JUST-IN-TIME)编译器的发展与逃逸分析技术的逐渐成熟,并不是所有对象都只在堆上分配了,比如:随着逃逸分析技术的逐渐成熟,在即时能被回收的对象也有可能会在虚拟机栈上分配。
由于现在都采用分代回收算法,所以从内存回收的角度来看,堆还可以细分为:新生代、老年代。新生代又可以分为:Eden 空间、From Survivor 空间、To Survivor 空间。
注意:1.8 中已经彻底将方法区的实现由之前的永久代改为元空间。
堆里面可能抛出的异常就是 OutOfMemoryError, 出现这种错误的表现形式主要有两种:
OutOfMemoryError: GC Overhead Limit Exceeded
:当 JVM 花太多时间执行垃圾回收并且只能回收很少的堆空间时,就会发生此错误。
java.lang.OutOfMemoryError: Java heap space
:假如在创建新的对象时, 堆内存中的空间不足以存放新创建的对象, 就会引发java.lang.OutOfMemoryError: Java heap space
错误。
方法区(METHOD AREA)
方法区和堆一样也是所有线程共享的一块区域,主要用来存储已经被虚拟机加载的类信息、常量(final 修饰的)、静态变量、即时编译器(JIT)编译后产生的代码等数据。虽然 Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap(非堆),目的应该是与 Java 堆区分开来。
永久代就是方法区域?
早些时候,很多开发者更愿意称方法区为“永久代”。其实“永久代”这个称呼的由来是因为 HotSpot 团队并不打算为方法区重新设计垃圾回收算法,为了在方法区中能够沿用堆中的分代回收算法,所以按照堆中的命名方式,将方法去称为“永久代”。对于 JRocket、J9 而言是不存在“永久代”的概念的,所以当 HotSpot 1.8 和 JRocket 合并时,就彻底放弃了“永久代”的概念(其实从 1.7 就已经开始了),取而代之是元空间,元空间使用的是直接内存。
方法区的垃圾回收很困难!!!
由于 Java 虚拟机规范对方法区的限制非常松,甚至可以不实现垃圾回收,一般而言,这个区域的内存回收很不令人满意,尤其是类型的卸载,条件非常苛刻,但是由于现代框架大量的依赖于 JIT 技术,导致方法区的占用比逐渐提高,所以对于方法区的回收至关重要。根据 Java 虚拟机规范规定,当方法区无法满足内存分配需求时,将抛出 OutOfMemoryError 异常。
运行时常量池(RUNTIME CONSTANT POOL)
JDK1.7 及之后版本的 JVM 已经将运行时常量池从方法区中移了出来,在 Java 堆(Heap)中开辟了一块区域存放运行时常量池。
这块区域在 1.7 之前原来是方法区的一部分,Class 文件中有一项信息是常量池(或者说是一张常量表,Class 文件以表存储数据)。
图 1-3:Class 文件常量池
运行时常量池存储的东西较为复杂,主要分为字面量和符号引用。
字面量
存放的字面量主要包括 常量(final 修饰的),比如:final int x = 1
、静态变量(static 修饰的),还有一些其他的字面量。
符号引用
符号引用主要包括:类的完全限定名、字段名称和描述符、方法名称和描述符,包括很多符号,比如:()
也可以看做符号引用。
字面量和符号引用将在类加载(ClassLoader 加载 Class 字节码文件)后进入方法区的运行时常量池中存放。不过,除了保存 Class 文件中描述的符号引用外,还会把翻译出来的直接引用也存储在运行时常量池中。运行时常量池相对于 Class 文件常量池一个重要的特征就是具备动态性,Java 语言并不要求常量一定产生于编译期的 Class 文件的常量池中,也并不是只有 Class 文件常量池中的常量才能够进入运行时常量池中,在线程执行方法的过程当中可能产生新的常量存放到运行时常量池中,例如:String 类的 intern() 方法。当运行时常量池无法申请到内存的时候就会抛出 OutOfMemoryError 异常。
直接内存(DIRECT MEMORY)
直接内存并不是 JVM 运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。而且也可能导致 OutOfMemoryError 错误出现。
在 JDK1.4 中新加入的 NIO(New Input/Output) 类,引入了一种基于通道(Channel) 与缓存区(Buffer) 的 I/O 方式,它可以直接使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样就能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆之间来回复制数据。
本机直接内存的分配不会受到 Java 堆的限制,但是,既然是内存就会受到本机总内存大小以及处理器寻址空间的限制。
总结
Java 虚拟机包含的内容很多,本篇文章也只是对 Java 内存管理模块的 Java 虚拟机运行时数据区做了简要的分析,关于内存管理模块的其他部分后续会继续更新,敬请期待!
参考
- 《深入理解 Java 虚拟机·JVM 高级特性与最佳实践(第 2 版》
- https://mp.weixin.qq.com/s/EZ4DDTC0CSqRQlqJLGSV-g
- https://blog.csdn.net/qq_41212104/article/details/80723644
- https://www.cnblogs.com/chanshuyi/p/jvm_serial_06_jvm_memory_model.html
- https://snailclimb.gitee.io/javaguide/#/docs/java/jvm/Java内存区域?id=写在前面-常见面试题
公众号
如果大家想要实时关注我更新的文章以及我分享的干货的话,可以关注我的公众号 我们都是小白鼠。公众号内有一些整理过的 原创精品脑图,不仅包含技术点的知识脉络,更多的底层原理的梳理,目前涵盖 Redis,RabbitMQ,Mysql,Java 虚拟机等 ,这些都是博主自己的学习笔记,整理的过程花费了很多心血,除此之外还有一些整理过的 面试题 以及日常开发常用到的一些 开发工具 等,在公众号内分别回复【技术脑图】、【面试题】、【开发工具】即可获取。
干活分享
Java 运行时数据区的更多相关文章
-
Jvm基础(1)-Java运行时数据区
最近在看<深入理解Java虚拟机>,里面讲到了Java运行时数据区,这是Jvm基本知识,把读书笔记记录在此.这些知识属于常识,都能查到的,如果我有理解不对的地方,还请指出. 首先把图贴上来 ...
-
读书笔记-浅析Java运行时数据区
作为一个 Java 为主语言的程序员,我偶尔也需要 用 C/C++ 写程序,在使用时让我很烦恼的一件事情就是需要对 new 出来的对象进行 delete/free 操作,我老是担心忘了这件事情,从而导 ...
-
Java运行时数据区
目录 1. 概述 2. Java内存结构 3. 程序计数器 4. Java虚拟机栈 5. 本地方法栈 6. 堆 7. 方法区 8. 运行时常量池 9. 直接内存 10. 总结 1. 概述 作为日常的J ...
-
【转】Java运行时数据区简介及堆与栈的区别
理解JVM运行时的数据区是Java编程中的进阶部分.我们在开发中都遇到过一个很头疼的问题就是OutOfMemoryError(内存溢出错误),但是如果我们了解JVM的内部实现和其运行时的数据区的工作机 ...
-
Java 运行时数据区和内存模型
运行时数据区是指对 JVM 运行过程中涉及到的内存根据功能.目的进行的划分,而内存模型可以理解为对内存进行存取操作的过程定义.总是有人望文生义的将前者描述为 "Java 内存模型" ...
-
JVM之Java运行时数据区(线程隔离区)
来源 JVM会在会在执行Java程序过程中把所管理的内存划分为若干区域,主要包括程序计数器(Program Counter Register),虚拟机栈(VM Stack),本地方法栈(Native ...
-
JVM内存布局(又叫Java运行时数据区)
JVM 堆中的数据是共享的,是占用内存最大的一块区域. 可以执行字节码的模块叫作执行引擎. 执行引擎在线程切换时怎么恢复?依靠的就是程序计数器. JVM 的内存划分与多线程是息息相关的.像我们程序中运 ...
-
JVM之Java运行时数据区(线程共享区)
JVM运行时区域各线程共享的区域包括堆区和方法区. 堆区 堆区最最主要的功能是存储对象实例[上篇也提到过],因此Java垃圾回收的主要战场就是在堆区,因此也有称为GC堆区.如果堆区的内存不够会出现Ou ...
-
Java运行时数据区概述
Java 虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途,如图所示: 程序计数器 程序计数器是一块比较小的内存空间,可以看作是当前线程所执行的字节 ...
随机推荐
-
SQL 事务回滚
事务(Transaction)是并发控制的单位,是用户定义的一个操作序列.这些操作要么都做,要么都不做,是一个不可分割的工作单位.通过事务,SQL Server能将逻辑相关的一组操作绑定在一起,以便服 ...
-
课程笔记:——Javascript 中的预解释1
1.预解释(变量提升):在当前作用域下,JS代码执行之前,浏览器首先会把所有带var和function关键字的进行提前的声明或者定义var num = 12;声明(declare): var num; ...
-
html中的块元素(Block)和内联元素(Inline)(转)
我们首先要了解,所有的html元素,都要么是块元素(block).要么是内联元素(inline).下面了解一下块元素.内联元素各自的特点: 块元素(block)的特点: 1.总是在新行上开始:2.高度 ...
-
为什么要使用SLF4J而不是Log4J
每一个Java程序员都知道日志对于任何一个Java应用程序,尤其是服务端程序是至关重要的,而很多程序员也已经熟悉各种不同的日志库如java.util.logging.Apache log4j.lo ...
-
SilverLight页面跳转(转载)
// Silverlight页面的跳转 // (Application.Current.RootVisualasIContent).Content=newDragControl(); //Silver ...
-
零基础学习hadoop到上手工作线路指导
零基础学习hadoop,没有想象的那么困难,也没有想象的那么容易.在刚接触云计算,曾经想过培训,但是培训机构的选择就让我很纠结.所以索性就自己学习了.整个过程整理一下,给大家参考,欢迎讨论,共同学习. ...
-
【解决】WordPress FTP连接服务器时出错,请检查设置,WordPress需要访问您网页服务器的权限
刚装好wordpress,发现后台预装了两个插件,想删掉,结果要登录FTP,死活登不上去,提示"连接服务器时出错,请检查设置,WordPress需要访问您网页服务器的权限",网上也 ...
-
Java数据结构漫谈-ArrayList
ArrayList是一个基于数组实现的链表(List),这一点可以从源码中看出: transient Object[] elementData; // non-private to simplify ...
-
Cocos2D &;amp; SpriteBuilder Developer Guide
https://www.makegameswith.us/docs/#!/cocos2d/1.0/overview
-
Leetcode 39 40 216 Combination Sum I II III
Combination Sum Given a set of candidate numbers (C) and a target number (T), find all unique combin ...