[[322423]]中国
在口试的技能,在问到对于JVM相干的问题,会发现不少的口试者都是机械的在记挂,稍一细问就戛然则止。属于死记硬背型的,预计是看书里记个大略的见识或者图,并莫得融合含义。
实质上这块内容,看见识的技能能对照一个简便的门径分析,不错更好的融合。底下我们初始。
市面上常见的JVM竹素里,对于JVM的体系缚构,一般分别红以下几个部分:
类加载器 门径计数器(Program Counter Register,简称PC Register) Java 假造机栈(Java Virtual Machine Stacks) 堆(Heap) 格局区(Method Area) 运行相同量池(Run-time Constant Pool) 腹地格局栈(Native Method Stack) 栈帧(Stack Frame) 奉行引擎(Execution Engine)其中2~7项,又称为运行时数据区,毕竟这些东西只消JVM 跑起来才会创建。这个分类,基本都是参照 Java 假造机模范。
若是干巴巴的记见识没啥道理,吃个饭可能就忘了。接下来用 1+2这个门径来试着融合它。
我们来看个入门Java 编程的技能都基本都写过的,访佛 Hello World的门径。
public 中国class HelloWorld { public static void main(String[] args) { int a = 1; int b = 2; int c = a + b; } }
先 javac 编译之后,再用javap -verbose HelloWorld 来不雅察一下, 你会看到访佛底下的输出内容:
public class HelloWorld minor version: 0 major version: 55 flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #2 // HelloWorld super_class: #3 // java/lang/Object interfaces: 0, fields: 0, methods: 2, attributes: 1 Constant pool: #1 = Methodref #3.#12 // java/lang/Object."<init>":()V #2 = Class #13 // HelloWorld #3 = Class #14 // java/lang/Object #4 = Utf8 <init> #5 = Utf8 ()V #6 = Utf8 Code #7 = Utf8 LineNumberTable #8 = Utf8 main #9 = Utf8 ([Ljava/lang/String;)V #10 = Utf8 SourceFile #11 = Utf8 HelloWorld.java #12 = NameAndType #4:#5 // "<init>":()V #13 = Utf8 HelloWorld #14 = Utf8 java/lang/Object { public HelloWorld(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=4, args_size=1 0: iconst_1 1: istore_1 2: iconst_2 3: istore_2 4: iload_1 5: iload_2 6: iadd 7: istore_3 8: return LineNumberTable: line 3: 0 line 4: 2 line 5: 4 line 6: 8 }
好嘞。我们都知说念,上头这些即是Java的字节码。有了上头这个输出的内容,你把我方念念像成假造机,来运行它中国,就融合了 Java 假造机里各个部分了。
最初,这部天职容,要奉行,一定得先读到内存里,负载读这些内容的,即是假造机的类加载器。
加载进来的其实是个二进制流,然后呢,需要把它整理成对应形势的内容才便捷使用嘛。比如这个类叫啥名字,接管了谁,都有什么格局,格局名字叫啥,内容是什么这些东西要找个方位放着。放哪好呢?格局区即是干这个的。
所谓的运行相同量池亦然格局区里的一块区域。往上看Constant Pool 在运行时会被默契成 Run-time Constant Pool。若是波及到对其他类的援用等等,会在加载之后再合资的技能,把这内部的记号援用鼎新成径直援用。
另外一些部分呢?概述来讲即是Java假造机栈,即是我们常说的栈,是用来奉行格局里的具体内容的。这一部分其实不错这么融合。Java 假造机,和我们果真的物理机访佛,都会把门径提供的教导奉行,只不外假造机是一个提供了一套有限教导集的软件。物理机基本都是基于寄存器奉行,而 Java 假造机的对于教导的奉行杀青是基于栈的。
既然是栈,那栈里要放点什么?没错,是栈帧,英文是 Frames,即是我们在使用 IDE debug 的技能看到的那一层一层的内容。
每个格局调用的技能,都会出现一帧,每一帧亦然个结构,格局奉行用到的东西都在内部。比如在 Debug 的技能,一般都会看到每个变量和值, 这些变量称为腹地变量(local variables),在上头的输出内容里也有stack=2, locals=4, args_size=1 我们看到locals即是腹地变量,args_size是格局参数的长度,还有一个即是操作数(stack),数值是栈的最大深度。
每个class 的大肆一个格局里,都会有 frame ,它们都有我方的 local variables 腹地变量表, 我方的operand stack 操作数栈,以及到run-time constant pool 运行相同量池的援用。虽然,也不错有一些推广信息,举例debug info。
那具体上头简便的一个 1+2 这个操作,对应到 jvm 教导有这些:
0: iconst_1 1: istore_1 2: iconst_2 3: istore_2 4: iload_1 5: iload_2 6: iadd 7: istore_3 8: return
具体现时奉行到第几条教导,需要有个象征,这个活儿让门径计数器给干了。这小子一直指向下一条行将奉行的教导。基本栈的杀青,上头的教导冒失是把常量1赋值给第一个变量,常量2赋值给第二个变量,之后,变量一入栈,变量二入栈,奉行iadd操作的技能,这两个数据出栈,完成乞降,再赋值给变量3,入栈,再复返。下次我们细说JVM教导的技能,再详备说说。这些教导的奉行,虽然离不开奉行引擎。
因为不需要奉行Native格局,是以我们一般毋庸腹地格局栈,这是给访佛JNI这些腹地格局杀青准备的。
你看,不雅察了1+2的经过,基本Java 假造机的结构是不是就融合了?:-) 若是依然记不住的话,你不错这么念念啊,Java 的天下里,平淡会说到堆和栈。那栈用来存啥呢?念念念念你 debug 技能看到的那一层层的帧, 然后再念念念念今天的1+2的奉行,应该就皆了。
本文转载自微信公众号「 Tomcat那些事儿」,不错通过以下二维码温雅。转载本文请相干 Tomcat那些事儿公众号。