首页 > 其他 > 详细

JVM 内存区域

时间:2021-06-02 09:23:46      阅读:22      评论:0      收藏:0      [点我收藏+]

运行时内存区域

Java 虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。
这些区域用途各异,创建和销毁的时间也不尽相同。根据<<Java虚拟机规范>> 规定,
Java虚拟机所管理的内存将包括以下几个运行时数据区域。

技术分享图片

程序计数器

程序计算器是一块较小的内存空间,可以看作是当前线程所执行的字节码的信号指示器。
字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。

Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间来实现的。在同一时间,
一个CPU核心只会执行一条线程中的指令。因此,为了避免多线程运行时的互相影响,
每个线程都会分配一个独立的程序计数器,独立存储。为线程所私有的内存。

Java虚拟机栈

在Java中,虚拟机栈的生命周期与线程相同。其描述的是Java 方法执行的线程内存模型:
每个方法被执行的时候,Java 虚拟机都会同步创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
方法的调用至执行完毕,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

  • 局部变量表存放了编译器可知的各种Java虚拟机基本数据类型、对象引用和returnAddress类型。(指向一条字节码指令的地址)
    局部变量表所需空间在编译器完成分配,运行期间不会改变其大小(即局部变量数不变)。

在JVM规范中,虚拟机栈区域规定了两类异常情况:如果线程请求的栈深度大于虚拟机所允许的深度,将跑出StackOverFlowError异常;
如果虚拟机栈容量可以动态扩展,当栈扩展却无法申请到足够的内存时,会抛出OOM异常。

本地方法栈

本地方法栈与虚拟机栈所发挥的作用十分相似,区别仅在于虚拟机栈为虚拟机执行Java方法服务,
而本地方法栈则是为虚拟机使用到的本地(native)方法服务。

本地方法栈中可使用任意的语言和数据结构,由虚拟机自行决定如何实现它。
在HotSpot中,直接将本地方法栈和虚拟机栈合二为一了。与虚拟机栈一样,本地方法栈中,
同样可能抛出SOE和OOM异常。

堆(Heap)虚拟机所管理的内存中最大的一块。所有线程共享这一内存区域,在虚拟机启动时便会创建。
堆的作用就是存放对象实例,Java中几乎所有的对象实例都在这里分配内存。这里的几乎是因为,
从实现角度,些许迹象表明日后可能出现值类型的支持,即便现在,由于JIT技术的进步,尤其是在逃逸分析技术的日渐强大,
栈上分配、标量替换等优化手段已经导致一些微妙变化悄然发生。所以说,Java对象实例都分配在堆上也渐渐变得不那么绝对。

堆是垃圾收集器所管理的内存区域,从内存回收角度,由于现代垃圾收集器大部分都基于分代收集理论设计,
所以Java堆中经常出现"新生代","老生代","永久代","Eden 空间","From Survivor空间"等名词,这类区域仅是一部分垃圾回收器的共同特性,
或者说设计风格而已,而非JVM具体实现的固有内存布局,也不是JVM规范中对堆的细致划分。在如今,垃圾回收技术与十年前已经不能
同日而语,HotSpot中也出现了不采用分代设计的新垃圾回收器,再按照这些提法可能就会有一些错误。

将堆细分的目的也只是为了更好地回收内存,或者更快地分配内存。堆大小可以是动态扩展的,当堆无法再扩展是会抛出OOM。

方法区

方法区(Method Area)用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
在JDK8 之前,HotSpot使用堆中的"永久代"来实现方法区,这样使得HotSpot的垃圾收集器能够像管理Java堆一样管理这部分内存,
省去了专门为方法区编写内存管理代码的工作。

在JDK8之后,HotSpot废弃了"永久代"的概念,将方法区移至由本地内存实现的原空间中。

运行时常量池

运行时常量池为(Runtime Constant Pool)方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,
还有一项常量池表(Constant Pool Table),用于存放编译期生成的各种字面量与符号引用,这部分内容在类加载后存放到方法区的
运行时常量池中。

运行时常量池相对于Class文件常量池有一个重要特征就是具备动态性,并不要求常量一定只有编译期才能产生,也就是说,
在运行期,也可以将新的常量放入池中,如String.intern()方法。

直接内存

直接内存(Direct Memory)并不是JVM运行时数据区的一部分,但这一区域也被频繁的使用,并且也有可能导致OOM。
在JDK 1.4中新加入的NIO类,引入了Channel和Buffer的IO方式,它可以使用Native函数库直接分配堆外内存
然后通过一个存储在Java 堆中的DirectByteBuffer 对象作为这块内存的引用进行操作。在部分场景中显著提高性能,
因为避免了在Java堆和Native堆中来回复制数据。

直接分配内存不会受到堆大小的限制,只会受到本机总内存大小以及处理器寻址空间的限制。
在服务器管理员配置虚拟机参数时,若只根据实际内存设置-Xmx等参数信息,却忽略掉直接内存时,
可能会使得各个区域内存总和大于物理内存,从而导致动态扩展时出现OOM异常。

JVM 内存区域

原文:https://www.cnblogs.com/cd-along/p/14838267.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!