以前学JVM的时候看过《深入理解JVM》,当时看的很模糊也记了些笔记,更像是为了应付面试。事实是确实把笔记都背上了,春招找实习的时候,内存管理、类加载、垃圾回收三连背一遍。后来自己做项目的时候,涉及到JVM的部分还是不怎么理解,最近重读了上面的书并且看了一些技术大佬的专栏,用博客记录下自己学习过程与思考。
本篇文章关注两个问题:
为了解释上面问题,假设现在我们有一个Main类,调用compute方法执行计算操作,代码如下:
public class Math { public static final Integer CONSTANT = 10; public int compute() { int a = 1; int b = 2; int c = (a + b) * 10; return c; } public static void main(String[] args) { Math math1 = new Math(); Math math2 = new Math(); math1.compute(); math2.compute(); } }
对于第一个问题: Class文件是一组以8位字节位基础的单位的二进制流,下图就是显示了如何生成字节码文件。
使用Sublime Text查看Math.class,图片只截取了部分,编辑器是使用16进制显示的。为了方便查看,我们使用 javap -c 指令对代码进行反汇编,就可以得得到可读性更强的文件。
那么Class文件被加载后在JVM中是如何存储的呢?我们以 HotSpot VM为例,这是目前使用最广泛的Java虚拟机。虚拟机主要由类装载子系统、运行时数据区和执行引擎三部分组成。JVM内存模型将运行时数据区分为五个部分,下面图中其中紫色部分是线程私有的,黄色是线程公有的。我们对着字节码文件详细的解释下这个5个部分分别存放的是什么。
栈 又叫做 线程栈 ,生命周期与线程相同。栈主要由 局部变量表、操作数栈、动态链接、方法出口 组成。当main方法运行时JVM会在栈内存区域给主线程分配一块内存,main方法和compute方法执行时,会创建单独的栈帧用于存储方法的一些信息。配合着字节码来说明:
程序计数器: 记录的是字节码指令正在执行或者即将执行的行号。
本地方法栈: 作用和虚拟机栈类似,为native修饰的方法服务。
方法区: JDK1.8及以后称为元空间,存储被虚拟机加载的类信息、常量、静态变量等。1.8以后方法区使用的是本机的内存。
堆: 堆是JVM内存模型中最大的一块,虚拟机启动时就会创建,存储的是大部分对象。
整个代码的执行流程在JVM内存中是这样的,不作更详细的解释了,关键信息在图中已标明。
参考资料:《深入理解Java虚拟机》第二版 周志朋
《深入拆解Java虚拟机》郑雨迪
《JVM虚拟机底层原理分析与性能调优》程序员诸葛