推荐阅读:JavaGuide | 程序员大彬 | 小林Coding
本篇文章记录JVM相关知识,以及一些面试常问问题。以面试题为出发点,并对相关问题进行扩展与总结,基本每个知识点都附上了我觉得写的很哇塞的文章链接有助于深入理解与扩展阅读。
1. JVM由哪些部分组成?其运行流程是什么?
答:
- JVM主要由: 类加载器、运行时数据区、执行引擎、本地库接口组成;
- 运行流程: ① 首先将
.java
源码文件编译成JVM可以解析的.class
字节码文件(由javac
命令将.java
编译成.class
的,例:javac helloworld.java); ② 类加载器会将编译好的.class
字节码文件加载到运行时数据区;③ 然后执行引擎将字节码文件解释为底层系统指令,交由CPU执行,并且需要调用其他语言的本地库接口来实现整个程序的功能;
扩展阅读:JVM的工作流程
2. 什么是程序计数器?它的作用是什么?
答:
- 作用: 用于记录下一条 JVM 指令的执行地址(也就是记录字节码的当前执行的行号),分支、循环、跳转、异常、线程恢复等都依赖于程序计数器;
- 特点: 线程私有;不会存在溢出。
扩展阅读:JVM程序计数器
3. 介绍一下Java堆内存
注意:
(1) 堆内存逻辑上由新生代 ( Young
),老年代 ( Old
)和永久代(Perm
)或者Metaspace(元空间
)组成. (逻辑上:堆=新生+老年+永久或者元空间)
(2)堆内存物理上由新生代 ( Young
)和老年代 ( Old
)组成,也就是堆内存的大小等于新生代的大小+老年代的大小(物理上:堆=新生+老年)
答:
- Java堆是
线程共享
的区域并且是GC主要的回收区
;主要用来保存对象实例、数组
等;当堆中没有内存空间可以分配给对象实例,也无法再扩展时,会抛出OutOfMemoryError
异常。 - 组成:JDK1.8以后由
年轻代
和老年代
组成;年轻代分为三个部分,一个Eden区
和两个大小相同的Survivor区
,根据JVM的策略,经过几轮垃圾回收后,仍然存活于Survivor区的对象将被移动到老年代。老年代区主要用来保存生命周期长
的对象;元空间用来保存类信息
、静态变量
、常量
、编译后的字节码
。 - JDK1.7和JDK1.8中Java堆内存的区别:JDK1.7中有一个永久代,存储
类信息
、静态变量
、常量
、编译后的字节码
。而JDK1.8移除了永久代,把数据存储到了本地内存的元空间中,防止内存溢出。
扩展阅读:深入浅出JVM(三)之堆内存(Heap)
4. 介绍一下虚拟机栈
每一个方法从调用开始至执行结束的整个过程,都对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息,在同一时刻、同一条线程中,只有位于栈顶的方法才是在运行的,只有位于栈顶的栈帧才是生效的,执行引擎所运行的所有字节码指令都只针对当前栈帧进行操作。虚拟机栈和栈帧的总体结构如下图:
答:
- 虚拟机栈是每个
线程运行时所需要的内存空间
,是先进后出
的;每个栈由多个栈帧
组成,对应着每次方法调用时占用的内存
;每个线程只能有一个活动栈帧
,对应着当前正在执行的那个方法; - 虚拟机栈是
线程私有
的,生命周期和线程相同
,每个方法被执行的时候会创建栈帧; - 栈帧保存的是执行方法的
局部变量表
、操作数栈
、动态连接信息
、方法返回地址信息
等; - 方法开始执行的时候会进栈,方法执行完出栈,
不需要垃圾回收
。
扩展阅读:Java虚拟机栈详解 | 栈帧详解 | 深入理解栈帧结构
5. 虚拟机栈内存分配得越大越好吗?
答:
- 非也,因为物理内存是固定大小,若分配过大,则可分配得线程数会相应减少。虽然栈内可进行更多次方法调用,但由于线程数减少,所以并不会提高效率。
未完待续