JVM相关面试题
一、详细介绍一下Java内存布局
根据JVM规范,JVM内存分为五部分:程序计数器、虚拟栈、本地方法栈、堆、方法区。如下图所示:
其中程序计数器、虚拟机栈、本地方法栈为线程私有,堆、方法区(元空间)属于线程共享区域。
- 程序计数器:程序计数器是虚拟机中唯一一个内存溢出(out of memory)的区域,它是一块较小的内存空间,记录了线程执行字节码的行号;每个线程都有自己程序计数器,因此这块内存区域是线程私有的;如果虚拟机正在执行一个Java方法,则程序计数器指向的正在执行的字节码的地址;如果执行的是一个Native方法,则程序计数器为Undefined。
- 虚拟机栈:虚拟机栈是Java方法执行的线程内存模型,一个方法从执行开始到执行完成就是一个栈帧(stack frame)从入栈到出栈的过程。栈帧中主要存储了局部变量表、操作数栈、动态链接、方法出口等信息。虚拟机栈的生命周期同线程一样,故也是线程私有的一块内存区域。
- 本地方法栈:本地方法栈同虚拟机栈类似,只不过服务的对象的Native方法,而虚拟机栈服务对象是Java方法。
- 堆:堆是实际开发过程中关注最多的地方。虚拟机创建的所有对象实例都在堆上分配内存空间,是一块线程共享的内存区域。
- 方法区(元空间):元数据区和堆空间一样,是线程共有内存区域,在JDK 8 之前,元数据区被称为方法区(永久代),里边存储了类型信息、常量、静态变量以及即时编译编译后的缓存数据等;方法区其实是堆空间的一个逻辑区域,所以方法区的大小还是受限于堆空间的大小。在JDK 8之后,使用本地内存实现方法区,此时方法区的大小与堆无关,首先与物理机器的内存空间。
PS:方法区的实现在JDK7之前使用永久代(PermGen)实现,JDK8之后使用元空间(Metaspace)实现。详细变化有以下几点:
- JDK8后将字符串常量常量池、静态变量从永久代移除
- 元空间使用本地内存实现,与堆无关
- JDK8后将类型信息移到元空间