JVM内存区域面试重点

Spring Wu 411 2021-02-03

程序计数器

重点: 主要是与字节码指令的执行相关

用于字节码解释器来指示线程下一步该做什么,比如:线程恢复、return、循环、异常处理等指令。

  • 为什么程序计数器是线程私有的?

在多线程环境下,线程之间会互相切换运行。并不是真正的“并行”。所以当线程切换回来恢复后,需要知道该线程上次已经执行到什么地方了,并且需要独立存储,线程之间不应该互相影响。所以在程序计数器中,每个线程都应该有一个程序计数器,这样才能保证线程正确执行指令。

  • 程序计数器的生命周期

程序计数器因为是线程私有的,所以当线程创建的时候也创建了一个程序计数器。当线程执行完毕后,该线程对应的程序计数器也应该被销毁掉。

虚拟机栈

重点:主要是与方法调用相关

用于存放局部变量表、操作数栈、方法出口等信息,局部变量中存储了在编译期间已知的基本数据类型、对象引用和指向了一条字节码指令的地址。当一个方法被调用时,该方法的调用过程既是虚拟机栈入栈和出栈的过程。

  • 为什么虚拟机栈是线程私有的?

个人见解而言,线程在操作一个方法时,虚拟机栈应当先把方法信息、方法中的字节码指令、和方法内部的一些变量先存贮起来,该线程才能正确的执行该方法。虚拟机栈为什么是线程私有的也显而易见,因为每个线程执行的字节码指令可能会是一样的,但是执行的数据不一定是一样的,所以肯定是要对线程私有。而且一定不能共享。

  • 虚拟机栈的生命周期

因为是线程私有的,所以当线程创建的时候也会创建虚拟机栈,当线程执行完毕时,虚拟机栈也应该被一同销毁。

本地方法栈

重点:主要与native方法相关

本地方法栈与虚拟机栈类似,本地方法栈存贮的是native方法的局部变量表、操作数栈、方法出口等信息。在Hotspot中,本地方法栈与虚拟机栈是合二为一的。

方法区

重点:主要与类、类的常量相关

堆主要存放了类信息和运行时常量池,类信息存储如:类的结构、接口、构造函数、方法等。运行时常量池主要存贮在编译期生成的字面量和符号引用,这块区域是线程共享的。

  • 什么是符号引用?

在编译器编译时期,编译器是不知道类中引用的其他类的实例的内存地址的,比如在:com.baidu.A类中引用了com.baidu.B类,在编译时期,因为程序还没有真正的运行,只是把java代码编译为了class文件而已。所以,编译器是不知道com.baidu.B的内存地址的,所以,在编译的时候,对于这种,就使用符号引用代替真实的内存地址(直接引用)。比如com.baidu.A类引用了com.baidu.B类,符号引用为xxx.xxx.B

  • 为什么是线程共享的?

类的常量使用final static修饰的变量,在编译器编译时期就已经知道该变量的值是无法被改变的。而且类中的方法、接口、构造函数在程序运行时,是无法改变的。所以这块内容是可以供所有线程使用的。

重点:主要与对象实例、数组对象相关

堆主要存放了对象实例、数组对象的信息,几乎每个对象实例和数组对象的内存都在是堆中分配的。这也是GC关照的主要区域。这块区域是线程共享的。

  • 为什么是线程共享的?

当一个线程需要把数据传递到另一个线程时,有两种方法:

  1. 共享内存:所有线程都可访问该内存的数据。
  2. 消息传递:线程A发送消息到给线程B,说需要什么数据,然后线程B把自己堆中的内容复制到线程A的堆中。

当然JVM使用的是第一种方式。