先上一张JVM体系结构图:html
1)运行时数据区:通过编译生成的字节码文件(class文件),由class loader(类加载子系统)加载后交给执行引擎执行。在执行引擎执行的过程当中产生的数据会存储在一块内存区域。这块内存区域就是运行时区域java
2)程序计数器:用于记录当前线程的正在执行的字节码指令位置。因为虚拟机的多线程是切换线程并分配cpu执行时间的方式实现的,不一样线程的执行位置都须要记录下来,所以程序计数器是线程私有的多线程
3)虚拟机栈:虚拟机栈是java方法执行的内存结构,虚拟机会在每一个java方法执行时建立一个“栈桢”,用于存储局部变量表,操做数栈,动态连接,方法出口等信息。当方法执行完毕时,该栈桢会从虚拟机栈中出栈。其中局部变量表包含基本数据类型和对象引用;函数
在java虚拟机规范中,对这个区域规定了两种异常状态:若是线程请求的栈的深度大于虚拟机容许的深度,将抛出StackOverFlowError异常(栈溢出),若是虚拟机栈能够动态扩展(如今大部分java虚拟机均可以动态扩展,只不过java虚拟机规范中也容许固定长度的java虚拟机栈),若是扩展时没法申请到足够的内存空间,就会抛出OutOfmMemoryError异常(没有足够的内存)post
4)本地方法栈:相似java方法的执行有虚拟机栈,本地方法的执行则对应有本地方法栈优化
5)方法区:用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。线程共享(看存储的数据就知道了)ui
java虚拟机规范对方法区的限制很是宽松,除了和java堆同样不须要连续的内存和能够选择固定大小或者可扩展外,还能够选择不实现垃圾收集。相对而言,垃圾收集在这个区域是比较少出现的,但并不是数据进入了方法区就如永久代的名字同样永久存在了。这区域的内存回收目标重要是针对常量池的回收和类型的卸载,通常来讲这个内存区域的回收‘成绩’比较难以使人满意。尤为是类型的卸载条件很是苛刻,可是这部分的回收确实是必要的。在sun公司的bug列表中,曾出现过的若干个严重的bug就是因为低版本的HotSpot虚拟机对此区域未完成回收致使的内存溢出。spa
6)java堆(java Heap):堆的主要做用是存放程序运行过程当中建立的对象实例,由于要存放的对象实例有可能会极多,所以也是虚拟机内存管理中最大的一块。而且因为硬件条件有限,因此须要不断回收已“无用”的实例对象来腾出空间给新生成的实例对象;所以java的垃圾回收主要是针对堆进行回收的(还有方法区的常量池),java堆不少时候也被称为GC堆(Garbage Collected Heap)。线程
7)类加载机制(Class Loader):类加载子系统是根据一个类的全限定名来加载该类的二进制流到内存中,在JVM中将造成一份描述Class结构的元信息对象(方法区),经过该元信息对象能够获知Class的结构信息:如构造函数,属性和方法等,Java容许用户借由这个Class相关的元信息对象间接调用Class对象的功能。htm
好!说了这么多关键字,再拿例子来说解一下这些关键字:
A.图1是咱们写的HelloWorld.java,经过IDE或命令:javac HelloWorld 编译生成16进制的HelloWorld.class(字节码文件,见图3),想读懂16进制字节可参考:一文让你明白java字节码 ;但通常IDE会自动转译成图2的指令;或者经过命令:javap -verbose HelloWorld 进行转译。
(图1)HelloWorld.java
(图2)HelloWorld.class
(图3)16进制的字节码:
B.接着,当咱们经过IDE或者命令:java HelloWorld 运行这个class文件时,字节码文件(class文件)经过类加载机制加载完毕交付给执行引擎执行;类加载机制把HelloWrold类的信息、静态变量(例子中没加)、常量(例子中没加,常量会加载到方法区的常量池,这和静态变量不同)等加载到方法区中,接下来若是须要建立该类的对象,须要经过new后面带的参数到方法区进行查找类相关信息。
C.类加载完后,虚拟机会检查程序的入口,虚拟机中程序的执行入口为main函数,如HelloWorld.class中,,执行引擎找到main函数开始执行指令,并生成一个“桢栈”入栈至虚拟机栈的栈顶;咱们能够看到(图2)在main方法下面的命令:0 new java.lang.StringBuilder [16] 表示建立一个String对象,建立的String对象实例会在java堆(Heap)中分配内存存储(Java对象在JVM中的建立过程能够看这篇文章:Java对象是怎么建立的(经过对象的建立,了解JVM内存结构)),并把该指令位置“0”记录到当前线程的程序计数器中;3 dup 而后把该对象的引用压入虚拟机栈中,并把该指令位置“3”记录到当前线程的程序计数器中;4 ldc <String "Hello"> [18] 从字符串常量池(从jdk1.7开始,字符串常量池被移动到java堆)加载字符串常量Hello,并更新指令位置到程序计数器;...若是执行过程当中有本地方法的指令,则会在本地方法栈中进行出入栈;这里有个点注意一下,请看main函数指令16的位置: 16 new java.lang.StringBuilder [31] 这里建立了一个StringBuilder对象,自jdk5开始已对这种类型的字符串拼接进行了优化,具体自行谷歌补充。
D.执行引擎执行指令过程当中,按需调用本地库接口以执行本地库方法,如new指令、输出屏幕等操做
以上就是一个HelloWorld执行过程在JVM中发生的事情。
参考:
《深刻理解Java虚拟机:JVM高级特性与最佳实践》一书
https://www.cnblogs.com/IUbanana/p/7067362.html