如何在后台做网站分页,账号权重查询入口,创建一个公司需要多少钱,o2o网站建设渠道Java中虚拟机在执行Java程序的过程中会将它所管理的内存区域划分为若干不同的数据区域。下面来介绍几个运行时数据区域。 一、程序计数器
1.1 简述
程序计数器#xff08;Program Counter Register#xff09;是一块较小的内存空间#xff0c;它的作用可以看做是当前线程所…Java中虚拟机在执行Java程序的过程中会将它所管理的内存区域划分为若干不同的数据区域。下面来介绍几个运行时数据区域。 一、程序计数器
1.1 简述
程序计数器Program Counter Register是一块较小的内存空间它的作用可以看做是当前线程所执行的字节码的行号指示器。
1.2 作用
大家都知道Java程序从源文件创建到程序运行要经过两大步骤 1. 源文件由编译器编译成字节码ByteCode。 2. 字节码由java虚拟机解释运行。 java程序编译运行过程 字节码解释器工作时就是通过改变这个 程序计数器 的值来选取下一条需要执行的字节码指令分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
1.3 特性
1.3.1 线程私有情况
由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的在任何一个确定的时刻一个处理器对于多核处理器来说是一个内核只会执行一条线程中的指令。因此为了线程切换后能恢复到正确的执行位置每条线程都需要有一个独立的程序计数器各条线程之间的计数器互不影响独立存储我们称这类内存区域为“线程私有”的内存。
1.3.2 异常情况
此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
二、Java虚拟机栈
2.1 描述
大家经常说的栈内存(Stack)就是指的虚拟机栈。虚拟机栈描述的是Java方法执行的内存模型每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链表、方法出口等信息。 不同的线程使用不同的栈 每一个方法从调用直至执行完成的过程就对应着一个栈帧在虚拟机栈中出栈入栈的过程。
2.2 组成
2.2.1 局部变量表
局部变量表是一组变量值存储空间用于存放方法参数和方法内部定义的局部变量。它可以存储各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型)和returnAddress。 1. reference类型虚拟机规范没有明确说明它的长度。它不等同于对象本身根据虚拟机种类的不同可能是一个指向对象起始地址的引用指针也可能是指向一个代表对象的句柄或其他与此对象相关的位置。 2. returnAddress指向了一条字节码指令的地址它是为字节码指令jsr、jsr_w和ret服务的。目前returnAddress类型已经很少见了很古老的java虚拟机曾经使用这几条指令来实现异常处理现在已经由异常表代替。 注意 系统不会为局部变量赋予初始值实例变量和类变量都会被赋予初始值。也就是说不存在类变量那样的准备阶段。 2.2.2 操作数栈
操作数栈中存放方法执行时的一些中间变量JVM 在执行方法时压入或者弹出这些变量。其实操作数栈是方法真正工作的地方执行方法时局部变量数组与操作数栈根据方法定义进行数据交换。 例如执行以下代码时它演示了虚拟机是如何把两个int类型的局部变量相加再把结果保存到第三个局部变量的。操作数栈的情况如下
int a 90;
int b 10;
int c a b;
字节码执行过程
begin
iload_0 // push the int in local variable 0 onto the stack
iload_1 // push the int in local variable 1 onto the stack
iadd // pop two ints, add them, push result
istore_2 // pop int, store into local variable 2
end
在这个字节码序列里前两个指令iload_0和iload_1将存储在局部变量中索引为0和1的整数压入操作数栈中其后iadd指令从操作数栈中弹出那两个整数相加再将结果压入操作数栈。第四条指令istore_2则从操作数栈中弹出结果并把它存储到局部变量区索引为2的位置。下图详细表述了这个过程中局部变量和操作数栈的状态变化图中没有使用的局部变量区和操作数栈区域以空白表示。 注意在这个图中操作数栈的顶部是在底边所以先压入的100位于上方。可以看出操作数栈其实是一个数据临时存储区存放一些中间变量方法结束了操作数栈也就没有啦。
2.2.3 动态链表
略待续
2.3 特性
2.3.1 线程私有情况
与程序计数器一样Java 虚拟机栈Java Virtual Machine Stacks也是线程私有的它的生命周期与线程相同
2.3.2 异常情况
如果现场请求的栈深度大于虚拟机所允许的深度将抛出StackOverflowError异常如果虚拟机栈可以动态扩展当前大部分的Java虚拟机都可动态扩展只不过Java虚拟机规范中也允许固定长度的虚拟机栈如果扩展时无法申请到足够的内存就会抛出OutOfMemoryError异常。
三、本地方法栈
3.1 描述
本地方法栈Native Method Stack)与虚拟机栈所发挥的作用是非常相似的其 区别 不过是虚拟机栈为虚拟机执行Java方法也就是字节码服务而本地方法栈则是为虚拟机使用到的Native方法服务。 虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定因此具体的虚拟机可以自由实现它。甚至有的虚拟机譬如Sun HotSpot虚拟机直接就把本地方法栈和虚拟机栈合二为一。
3.2 特性
3.2.1 线程私有情况
和虚拟机栈一样。
3.2.2 异常情况
与虚拟机栈一样本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。
四、Java堆
4.1 描述
对于大多数应用来说Java堆Java Heap是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域在虚拟机启动时创建。
4.2 作用
此内存区域的唯一目的就是存放对象实例几乎所有的对象实例都在这里分配内存。Java堆是垃圾收集器管理的主要区域因此很多时候也被称做“GC堆”Garbage Collected Heap。 Java堆的组成 ① 如果从内存回收的角度看由于现在收集器基本都是采用的分代收集算法所以Java堆中还可以细分为新生代和老年代 ② 如果从内存分配的角度看线程共享的Java堆中可能划分出多个线程私有的分配缓冲区Thread Local Allocation BufferTLAB。
4.3 特性
4.3.1 线程私有情况
Java堆是被所有线程共享的一块内存区域可以被线程之间共享。
4.3.2 异常情况
Java堆可以处于物理上不连续的内存空间中只要逻辑上是连续的即可就像我们的磁盘空间一样。在实现时既可以实现成固定大小的也可以是可扩展的不过当前主流的虚拟机都是按照可扩展来实现的通过-Xmx和-Xms控制。 如果在堆中没有内存完成实例分配并且堆也无法再扩展时将会抛出OutOfMemoryError异常。
五、方法区
5.1 描述
同 Java 堆一样方法区也是全局共享的一块内存区域。
5.2 作用
它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。与Java堆不一样之处在于方法区并不是垃圾回收的主要区域。 相对而言垃圾回收在这个区域是比较少出现的这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载。 一般来说该区域的垃圾回收的“成绩”比较难以令人满意尤其是类型的卸载条件相当苛刻但这部分区域的回收确实是必要的。由于方法区的这些特性很多人也把方法区称为“永久代”Permanent Generation。 HotSpot虚拟机的设计团队就选择把GC分代收集扩展至方法区或者说使用永久代来实现方法区这样HotSpot的垃圾收集器可以像管理Java堆一样管理这部分内存能够省去专门为方法区编写内存管理代码的工作。
5.3 特性
5.3.1 线程私有情况
与Java堆一样是各个线程共享的内存区域。
5.3.2 异常情况
与java堆类似不需要连续的内存和可以选择固定大小或可拓展外还可以选择不实现垃圾回收。 根据虚拟机规范的规定当方法区无法满足内存分配的需求时将抛出OutOfMemoryError异常。
5.4 运行时常量池
运行时常量池Runtime Constant Pool也是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外还有一项信息是常量池Constant Pool Table用于存放编译期生成的各种字面量和符号引用这部分将在类加载后进入方法区运行时常量池中存放。
5.4.2 特性
常量池中保存了Class文件中描述的符号引用和翻译出来的直接引用。这里不太懂运行时常量池具备动态性。程序在运行期间也可能把新的常量放入池中。
六、直接内存
6.1 描述
直接内存Direct Memory并不是虚拟机运行时数据区的一部分它指的是主机的物理内存。之所以拿到这里来说是因为可能会导致OutOfMemoryError异常出现所以放到这里一并讲解。
6.2 如何使用
那么什么时候会直接使用到物理内存呢 在NIO(New Input/Output)类中引入了一种基于通道Channel与缓冲区Buffer的I/0方式它可以使用Native函数库直接分配堆外内存然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样在一些场景中显著提高性能因为避免了在Java堆和Native堆中来回复制数据。
6.3 异常情况
当各个内存区域总和大于物理内存限制包括物理的和操作系统级的限制从而导致动态扩展时出现OutOfMemoryError异常。
七、总结
最后让我们通过表格来大致回顾一下Java虚拟机中内存的特性吧 内存区域 线程私有情况 异常情况 特性简述 程序计数器 线程私有 唯一一个没有异常情况的区域 当前线程所执行字节码的行号指示器 Java虚拟机栈 线程私有 StackOverflowError或OutOfMemoryError异常 用于存储局部变量表、操作数栈、动态链表、方法出口等信息为虚拟机栈执行Java方法服务 本地方法栈 线程私有 StackOverflowError或OutOfMemoryError异常 为虚拟机使用到的Native方法(字节码)服务 Java堆 线程共享 OutOfMemoryError异常 存放对象实例和主要的垃圾回收区域 方法区 线程共享 OutOfMemoryError异常 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。和Java堆有相似特性但不是垃圾回收的主要区域 运行时常量池 线程共享 OutOfMemoryError异常 方法区的一部分和方法区特性类似。保存了Class文件中描述的符号引用和翻译出来的直接引用同时具有动态性。 直接内存 线程共享 OutOfMemoryError异常 受限于物理内存NIO流可以直接操纵用以提高性能
参考链接 1. 栈帧、局部变量表、操作数栈 2. Java编译器、JVM、解释器 3. Java程序编译和运行的过程