JAVA详解JVM工作原理和流程

说起JAVA,人们首先想到的是,java是一门编程语言,然而事实上java是一种技术,它由四部分组成:1.java编程语言、2.java类文件格式、3.java虚拟机、4.java应用程序接口。他们关系如下图所示:

运行期环境代表着JAVA平台,开发人员编写java代码(.java文件),然后再将其编译成字节码文件(.class),再然后字节码被装入内存,一旦字节码进入虚拟机,就会被解释器解释执行,或者是被即时代码发生器,准换成机器代码执行。java平台由java虚拟机与java应用程序接口搭建,java语言则是进入这个平台的通道,用JAVA语言编写并编译的程序可以运行在这个平台上,这个平台结构如下图所示:

JAVA平台结构图
                                  应用程序和小应用程序
Java基本API Java标准扩展API
Java基础类 Java标准扩展类
                                         Java虚拟机
                                           移植接口
适配器 适配器 适配器 适配器
浏览器 操作系统  
操作系统 操作系统  
硬件 硬件 硬件  
浏览器上的JAVA 桌面操作系统上的JAVA 小型操作系统上的java Java操作系统的Java

在Java平台结构中可以看出Java虚拟机处于核心位置,是程序与底层操作系统和硬件无关的关键,它的下方是移植接口,移植接口由两部分组成:适配器与Java操作系统,A,人们首先想到的是,java是一门编程语言,然而事实上java是一种技术,它由四部分组成:1.java编程语言、2.java类文件格式、3.java虚拟机、4.java应用程序接口。他们关系如下图所示:

在Java平台结构中可以看出Java虚拟机处于核心位置,是程序与底层操作系统和硬件无关的关键,它的下方是移植接口,移植接口由两部分组成:适配器与Java操作系统,其中依赖平台的部分称为适配器;JVM通过移植接口在具体的平台和操作系统上实现,JVM的上方是Java基础类以及相关Api,用Java api编写的应用程序和小程序无需考虑 JAVA底层平台,而可以在任何java平台上运行;JVM起到的作用就是将程序与操作系统分离,从而实现Java语言平台无关性的功能;

JVM在其生命周期内有一个明确的任务就是运行JAVA程序,因此当JAVA程序启动的时候就会产生一个JVM实例,当程序运行结束以后此实例也跟着消失了。下面我来从JVM的体系结构与它的运行过程这两个方面来对它进行比较深入的研究。

1.Java虚拟机的体系结构

每个JVM都有两种机制

a.类装载子系统 :装载适合名称的类或者接口

b.执行引擎:负责执行已装载的类或接口中包含的指令

每个JVM都包含:

方法区、JAVA堆、JAVA栈、本地方法栈、指令计数器(记录每个线程执行到的位置)以及其它隐藏的寄存器

对于JVM的学习,在我看来这几个部分最重要:

A. java代码编译和执行的整个过程 

B. JVM内存管理和垃圾回收机制

下面分别对这几部分进行说明:

2.Java代码编译和执行的整个过程

也正是前面所说,java代码编译和执行的整个过程大概是:开发人员编写Java代码(.java文件),然后将之编译成字节码文件(.class文件)再然后字节码装入内存,一旦字节码进入虚拟机,它就会被解释器解释执行,或者被即时代码发生器转换为机器代码执行;

(1)Java代码编译是由Java源码编译器来完成的,也就是java代码到字节码(.class)文件的过程,流程如下图所示:

(2) Java字节码的执行 是由JVM的执行引擎来完成的

 Java代码编译和执行的整个过程包含以下三个重要机制:

* Java源码编译机制

* 类加载机制

* 类执行机制

(1)java源码编译机制

Java源码编译由以下三个过程组成

* 分析和输入到符号表

* 注解处理

* 语义分析和s生成Class文件

最后生成的class文件的组成结构:

a .结构信息:包含class文件格式、版本号及各部分的数量与大小的信息

b .元数据:对应JAVA源码中声明与常量的信息,包含类/继承的超类/实现的接口的声明信息,域和方法声明信息和常量池

c .方法信息:对应java源码中语句和表达式对应的信息,包含字节码,异常处理表,求值栈与局部变量区大小,求值栈的类型记录,调试符号信息。

JVM类加载是通过ClassLoader 及其子类来完成的,类的层次关系和加载顺序由下图来描述:

1.Bootstrap ClassLoader

负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类

2.Extension ClassLoader

负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar 或-Djava.ext.dirs指定目录下的jar包

3.App ClassLoder

负责加载classpath中指定的jar包及目录中class

4.Custom ClassLoader

属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader

加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader 到 BootStrap ClassLoader 逐层检查,只要某个classloader已加载就视为是已加载此类,保证此类只所有classLoader加载一次。而加载顺序是自顶向下,也就是由上层来逐层尝试加载此类;

(3)类执行机制

JVM是基于堆栈的虚拟机,JVM为每个新创建的线程都分配一个堆栈,也就是说,对于每个java程序来说,它的运行是通过对堆栈的操作来实现的。堆栈以帧为单位保存线程状态,JVM对堆栈只进行两种操作;以帧为单位的压栈与出栈操作。

JVM执行class字节码,线程创建后,都会产生程序计数器(PC)和栈(stack),程序计数器存放下一条执行的指令在方法内的偏移量,栈中存放着一个个栈帧,每个栈帧对应着每个方法的每次调用,而栈帧又是局部变量区和操作数栈两部分组成,局部变量区用于存放方法中的局部变量和参数,操作数栈中用于存放方法的执行过程中产 的中间结果,栈的结构如下图所示:

JVM内存管理以及垃圾回收机制:

JVM内存结构分为:方法区(method),栈内存(stack),堆内存(heap),本地方法栈(java中的jni调用),结构图如下所示:

(1)堆内存(heap)

所有通过new创建的对象的内存都在堆中分配,其中大小可以通过-Xmx 和 -Xms来控制。

操作系统有一个记录空闲内存地址的链接,当系统收到程序的申请时,会遍历链表,寻找第一个空间大于所申请空间的堆节点,然后将该节点从空闲节点链表中删除,并将该节点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首个地址处记录本次分配的大小,这样代码中的delete语句才能正确的释放本地内存空间。但由于找到的堆节点的大小不一定正好等于申请的大小,系统会自动将多余的那部分重新放入空闲链表中。这时由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,它不是堆,也不是栈,而是直接在进程的地址空间中保留一块内存,虽然这种方法用起来最不方便,但是速度最快,也是最灵活的。堆内存是向高地址扩展的数据结构,是不连续的内存区域。由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址到高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

(2)栈内存

在windows下,栈是向低地址扩展的数据结构,是一块连续的内存区域。这句话的意思是栈顶的地址和栈的最大内容量是系统预先规定好的,在WINDOWS下,栈的大小是固定的(是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获取否空间较小。只要栈的剩余空间大于所申请的空间,系统将为程序提供内存,否则将报异常提示溢出。由系统自动分配,速度较快。但程序员无法控制。

堆内存与栈内存需要说明:

基础数据类型直接在栈空间分配,方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收。引用数据类型,需要用new来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类型变量。方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完成后从栈空间回收。局部变量new出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间等待GC回收。方法调用时传入的literal参数,先在栈空间分配,在下方调用完成后从栈空间收回。局部变量new出来时,在栈空间和堆空间 中分配空间,在下方调用完成后从栈空间回收。字符串常量、static的DATA区域分配,this在堆空间分配。数组既在栈空间分配数组名称,又在堆空间分配数组实际的大小。

(3)本地方法栈(java中的jni调用)

用于支持native方法的执行,存储了每个native方法调用的状态。对于本地方法接口,实现JVM并不要求一定要有它的支持,甚至可以完全没有。sun公司实现Java本地接口(JNI)是出于可移植性的考虑,当然我们也可以涉及处其它的本地接口来代替Sun公司的JNI。但是这些涉及与实现是比较复杂的事情,需要确保垃圾回收器不会将那些正在被本地方法调用的对象释放掉。

(4)方法区(method)

它保存方法代码(编译后的java代码)和符号表。存放了要加载的类信息、静态变量、final类型的常量、属性和方法信息。JVM用持久代(Permanet Generation)来存放方法区,可通过-XX:PermSize 和 -XX:MaxPerSize来指定最小值和最大值。

垃圾回收机制:

堆里聚集了所有由应用程序创建的对象,JVM也有对于的指令,比如:new,newarray,anewarray和multianewarray,然而并没有像C++的delete,free等释放空间的指令,Java的所有释放都由GC来做,GC除了做回收内存之外,另外一个重要的工作就是内存的压缩,这个在其他的语言中也有类似的实现,相比C++不仅好用,而且增加了安全性,当然她也有弊端,比如性能这个大问题。

 

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读