2.谈谈对运⾏时数据区的理解?

Tip:这道题是⾮常重要的题⽬,⼏乎问到 Java 虚拟机这块都是会被问到的。建议不要简单的只回答⼏个区域的名

称,最好展开的讲解下,下⾯的答案是⽐较详细的,根据⾃⼰的理解回答其中某⼀段即可

1. 程序计数器

程序计数器(Program Counter Register):是⼀块较⼩的内存空间,它可以看作是当前线程所执⾏的字节码的⾏

号指示器。

字节码解释器⼯作时就是通过改变这个计数器的值来选取下⼀条需要执⾏的字节码指令。程序的分⽀、循环、跳

转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

由于 Java 虚拟机的多线程是通过线程轮流切换并分配处理器执⾏时间的⽅式来实现的,在任何⼀个确定的时刻,

⼀个处理器都只会执⾏⼀条线程中的命令。因此,为了线程切换后能恢复到正确的执⾏位置,每条线程都需要有⼀

个独⽴的程序计数器,各线程之间的计数器互不影响,独⽴存储,我们程这块内存区域为“线程私有”的内存。

此区域是唯⼀ ⼀个虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域。

 

2. Java 虚拟机栈

Java 虚拟机栈(Java Virtual Machine Stacks):描述的是Java⽅法执⾏的内存模型:每个⽅法在执⾏的同时都会

创建⼀个帧栈(Stack Frame)⽤于存储局部变量表、操作数栈、动态链接、⽅法出⼝等信息。每⼀个⽅法从调⽤

直⾄执⾏完成的过程,就对应着⼀个栈帧在虚拟机栈中⼊栈到出栈的过程。它的线程也是私有的,⽣命周期与线程

相同。

局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、

double)、对象引⽤和 returnAddress 类型(指向了⼀条字节码指令的地址)。

Java 虚拟机栈的局部变量表的空间单位是槽(Slot),其中 64 位⻓度的 double 和 long 类型会占⽤两个 Slot。局

部变量表所需内存空间在编译期完成分配,当进⼊⼀个⽅法时,该⽅法需要在帧中分配多⼤的局部变量是完全确定

的,在⽅法运⾏期间不会改变局部变量表的⼤⼩。

Java虚拟机栈有两种异常状况:如果线程请求的栈的深度⼤于虚拟机所允许的深度,将抛出 StackOverflowError

异常;如果扩展时⽆法申请到⾜够的内存,就会抛出 OutOfMemoryError 异常。

 

3. 本地⽅法栈

本地⽅法栈(Native Method Stack):与虚拟机栈所发ഀ的作⽤是⾮常相似的,它们之间的区别只不过是虚拟机

栈为虚拟机执⾏Java⽅法(也就是字节码)服务,⽽本地⽅法栈则为虚拟机使⽤到的 Native ⽅法服务。

Java 虚拟机规范没有对本地⽅法栈中⽅法使⽤的语⾔、使⽤的⽅式和数据结构做出强制规定,因此具体的虚拟机可

以⾃由地实现它。⽐如:Sun HotSpot 虚拟机直接把Java虚拟机栈和本地⽅法栈合⼆为⼀。

与Java虚拟机栈⼀样,本地⽅法栈也会抛出StackOverflowError和 OutOfMemoryError 异常。

 

4. Java

Java堆(Java Heap):是被所有线程所共享的⼀块内存区域,在虚拟机启动时创建。此内存区域的唯⼀⽬的就

是:存放对象实例,⼏乎所有的对象实例都在这⾥分配内存。

Java 堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC”堆(Garbage Collected Heap)。从内存回收

的⻆度看,由于现在收集器基本都采⽤分代收集算法,所以 Java 堆中还可以细分为:新⽣代和⽼年代。从内存分

配⻆度来看,线程共享的 Java 堆中可能划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,

TLAB)。不过⽆论如何划分,都与存放的内容⽆关,⽆论哪个区域,存储的都仍然是对象实例,进⼀步划分的⽬

的是为了更好地回收内存,或者更快地分配内存。

Java 虚拟机规定,Java 堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。在实现时,可以是固定

⼤⼩的,也可以是可扩展的。如果在堆中没有完成实例分配。并且堆也⽆法扩展时,将会抛出

OutOfMemoryError 异常。

 

5. ⽅法区

⽅法区(Method Area):与 Java 堆⼀样,是各个线程共享的内存区域,它⽤于存储已被虚拟机加载的类信息、

常量、静态变量、即时编译器编译后的代码等数据。

虽然 Java 虚拟机规范把⽅法区描述为堆的⼀个逻辑部分,但是它却有⼀个别名叫做 Non-Heap(⾮堆),其⽬的

应该就是与 Java 堆区分开来。

Java 虚拟机规范对⽅法区的限制⾮常宽松,除了和 Java 堆⼀样不需要连续的内存和可以选择固定⼤⼩或者可扩展

外,还可以选择不实现垃圾收集。这个区域的内存回收⽬标主要是针对常量池的回收和对类型的卸载。

根据Java虚拟机规范规定,当⽅法区⽆法满⾜内存分配需求时,将抛出OutOfMemoryError异常。

运⾏时常量池:运⾏时常量池(Runtime Constant Pool):是⽅法区的⼀部分。Class ⽂件中除了有类的版

本、字段、⽅法、接⼝等描述信息外,还有⼀些信息是常量池,⽤于存放编译期⽣成的各种字⾯量和符号引

⽤,这部分内容将在类加载后进⼊⽅法区的运⾏时常量池中存放。

Java 虚拟机对 Class ⽂件每⼀部分(⾃然也包括常量池)的格式都有严格的规定,每⼀个字节⽤于存储哪种

数据都必须符合规范上的要求才会被虚拟机认可、装载和执⾏。

 

直接内存:直接内存(Direct Memory):并不是虚拟机运⾏时数据区的⼀部分,也不是 Java 虚拟机规范中定

义的内存区域。但是这部分内存也频繁地使⽤,⽽且也可能导致 OutOfMemoryError 异常。

本地直接内存的分配不会受到 Java 堆⼤⼩的限制,但是,既然是内存,肯定还是会受到本机总内存⼤⼩以及

处理器寻址空间的限制。如果各个内存区域总和⼤于物理内存限制,从⽽导致动态扩展时出现

OutOfMemoryError 异常。

 

 

posted @ 2021-08-10 11:46  RainsX  阅读(118)  评论(0)    收藏  举报