前言
各位大大或者小白算是都知道,同比而言,java在各种各种的编程语言里算是极好入门的,有面向对象,没有让人脑瓜疼的指针,有自己的垃圾回收机制,有虚拟机,敲!重!点!了!——虚!拟!机!,一次编译处处运行这个功能对于程序员来说简直不要太友好,小白们(包括我自己)只要随手找个视频,分分钟一个helloword就出来了,然后学会CRUD,SSM什么的就可以走向工作岗位可以算是java入门了
然鹅,然鹅啊,同学们,Java入门到大神之间是有很多道不可逾越的鸿沟的为了跨越一道道鸿沟,我们必先拿下java虚拟机啊喂!拿下虚拟机这才算是再次入门了
JVM基本原理
as we all konw,所有程序的执行,最终都不过是寄存器里的001010101010,又有不同平台的机器码又是不一样的,那么java怎么就做到了一次编译处处运行呢?并不是java的语言是多通用,而是和java的运行流程相关:
java程序经过编译之后,将java代码编译为字节码也就是class文件,然后在不同的操作系统上依靠不同的java虚拟机进行解释,最后再转换为不同平台的机器码,最终得到执行。如此就是java的运行原理了。
然而光知道这个可是不够的,随手来一段helloworld继续了解下java编译到运行经过了哪些步骤好了:
java代码通过编译(javac HelloWorld.java)之后生成字节码文件(.class文件),通过:java HelloWorld执行,此时java根据环境变量找到jvm.cfg,windows下的路径:%JAVA_HOME%\jre\lib\amd64\jvm.cfg,然后随手找个编辑器打开看内容(#注释的内容咱不管了哈):
-server KNOWN -client IGNORE
其中-server KNOWN就表示名称为server的jvm可用,然后%JAVA_HOME%\jre\bin\server目录下可以找到jvm的主要实现——jvm.dll文件了,接下来会初始化JVM,并且获取JNI接口,JNI接口就是java本地接口,一般用于java和操作系统、硬件交互,JVM通过JNI从硬盘上找到编译好的.class文件,然后放CD一样把class文件载入jvm,然后找到main方法,最后执行。
Java基本结构
可能通过上面的描述,大家对JVM运行流程有了一个粗略的认识,那么JVM内部到底是怎么执行一个class文件的呢,也就是上图中最后一步第6步的内部细节是怎样的呢?要了解这个问题,我们首先得看一下JVM的内部结构:
讲真,从PDF上弄下来的一张极度不清晰的图片也是很对不起~
从这个结构不难看出,class文件被jvm装载以后,经过jvm的内存空间调配,最终是由执行引擎完成class文件的执行。当然这个过程还有其他角色模块的协助,这些模块协同配合才能让一个java程序成功的运行,下面就详细介绍这些模板。
JVM内存空间包含:方法区、堆、虚拟机栈、本地方法栈、程序计数器。
方法区是各个线程共享的区域,存放类信息、常量、静态变量。
java堆也是线程共享的区域,我们的类的实例就放在这个区域,可以想象你的一个系统会产生很多实例,因此java堆的空间也是最大的。如果java堆空间不足了,程序会抛出OutOfMemoryError异常。
java虚拟机栈是每个线程私有的区域,它的生命周期与线程相同,一个线程对应一个java栈,每执行一个方法就会往栈中压入一个元素,这个元素叫“栈帧”,而栈帧中包括了方法中的局部变量、用于存放中间状态值的操作栈,这里面有很多细节,我们以后再讲。如果java栈空间不足了,程序会抛出StackOverflowError异常,想一想什么情况下会容易产生这个错误,对,递归,递归如果深度很深,就会执行大量的方法,方法越多java栈的占用空间越大。
本地方法栈角色和java虚拟机栈类似,只不过它是用来表示执行本地方法的,本地方法栈存放的方法调用本地方法接口,最终调用本地方法库,实现与操作系统、硬件交互的目的。
程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。
执行引擎当然就是根据PC寄存器调配的指令顺序,依次执行程序指令。
OKl了,聊完了Java虚拟机的基本运行流程和内部结构,就先到此为止,第二章结束(第一章?Java历史与展望以及自己编译一个jdk什么的求放过~)