JVM是我们Javaer的最基本功底了,刚开始学Java的时候,一般都是从“Hello World”开始的,然后会写个复杂点class,然后再找一些开源框架,比如Spring,Hibernate等等,再然后就开发企业级的应用,比如网站、企业内部应用、实时交易系统等等,直到某一天突然发现做的系统咋就这么慢呢,而且时不时还来个内存溢出什么的,今天是交易系统报了StackOverflowError,明天是网站系统报了个OutOfMemoryError,这种错误又很难重现,只有分析Javacore和dump文件,运气好点还能分析出个结果,运行遭的点,就直接去庙里烧香吧!每天接客户的电话都是战战兢兢的,生怕再出什么幺蛾子了。我想Java做的久一点的都有这样的经历,那这些问题的最终根结是在哪呢?——JVM。
所谓虚拟机,就是一台虚拟的计算机,他是一款软件,用来执行一系列虚拟计算机指令。大体上,虚拟机可以分为系统虚拟机和程序虚拟机。大名鼎鼎的visual box、vmware就属于系统虚拟机,他们完全是对物理计算机的仿真,提供了一个可运行完整操作系统的软件平台。程序虚拟机的代表就是java虚拟机,他专门为执行单个计算机程序而设计,在java虚拟机中执行的指令我们称为java字节码指令。
JVM全称是Java Virtual Machine,Java虚拟机,也就是在计算机上再虚拟一个计算机,这和我们使用VMWare不一样,那个虚拟的东西你是可以看到的,这个JVM你是看不到的,它存在内存中。我们知道计算机的基本构成是:运算器、控制器、存储器、输入和输出设备,那这个JVM也是有这成套的元素,运算器是当然是交给硬件CPU还处理了,只是为了适应“一次编译,随处运行”的情况,需要做一个翻译动作,于是就用了JVM自己的命令集,这与汇编的命令集有点类似,每一种汇编命令集针对一个系列的CPU,比如8086系列的汇编也是可以用在8088上的,但是就不能跑在8051上,而JVM的命令集则是可以到处运行的,因为JVM做了翻译,根据不同的CPU,翻译成不同的机器语言。
JVM中我们最需要深入理解的就是它的存储部分,存储?硬盘?NO,NO,JVM是一个内存中的虚拟机,那它的存储就是内存了,我们写的所有类、常量、变量、方法都在内存中,这决定着我们程序运行的是否健壮、是否高效,接下来的部分就是重点介绍之。
我们先把JVM这个虚拟机画出来,如下图所示:
从这个图可以看出,JVM是运行在操作系统之上的,它与硬件没有直接的交互。
我们再来看下JVM有哪些组成部分,如下图所示:
该图参考了网上广为流传的JVM构成图,大家看这个图,整个JVM分为四部分:
类加载器的作用是加载类文件到内存,比如编写一个HelloWord.java程序,然后通过javac编译成class文件,那怎么才能加载到内存中被执行呢?Class Loader承担的就是这个责任,那不可能随便建立一个.class文件就能被加载的,Class Loader加载的class文件是有格式要求,在《JVM Specification》中式这样定义Class文件的结构:
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
需要详细了解的话,可以仔细阅读《JVM Specification》的第四章“The class File Format”,这里不再详细说明。
友情提示:Class Loader只管加载,只要符合文件结构就加载,至于说能不能运行,则不是它负责的,那是由Execution Engine负责的。
执行引擎也叫做解释器(Interpreter),负责解释命令,提交操作系统执行。执行引擎是java虚拟机的最核心组件之一,它负责执行虚拟机的字节码。现代虚拟机为了提高执行效率,会使用即时编译技术将方法编译成机器码后再执行。
本地接口的作用是融合不同的编程语言为Java所用,它的初衷是融合C/C++程序,Java诞生的时候是C/C++横行的时候,要想立足,必须有一个聪明的、睿智的调用C/C++程序,于是就在内存中专门开辟了一块区域处理标记为native的代码,它的具体做法是Native Method Stack中登记native方法,在Execution Engine执行时加载native libraies。目前该方法使用的是越来越少了,除非是与硬件有关的应用,比如通过Java程序驱动打印机,或者Java系统管理生产设备,在企业级应用中已经比较少见,因为现在的异构领域间的通信很发达,比如可以使用Socket通信,也可以使用Web Service等等,不多做介绍。
运行数据区是整个JVM的重点。我们所有写的程序都被加载到这里,之后才开始运行,Java生态系统如此的繁荣,得益于该区域的优良自治,下一章节详细介绍之。
整个JVM框架由加载器加载文件,然后执行器在内存中处理数据,需要与异构系统交互是可以通过本地接口进行,瞧,一个完整的系统诞生了!
虽然java语言和java虚拟机有着密切的联系,但两者是完全不同的内容。java虚拟机是一台执行java字节码的虚拟计算机,它拥有独立的运行机制,其运行的java字节码也未必由java语言编译而成,像Groovy、Scala等语言生成的java字节码也可以由java虚拟机执行。立足于java虚拟机,可以产生各种各样的跨平台语言。除了语言特性各不相同外,他们可以共享java虚拟机带来的跨平台性、优秀的垃圾回收器,以及可靠的即时编译器。
因此,与java语言不通,java虚拟机是一个高效的、性能优异的、商用级别的软件运行和开发平台。
java虚拟机规范的主要内容大概有以下几个部分:
1) 加载.class文件
2) 管理并分配内存
3) 执行垃圾收集
JRE(java运行时环境)由JVM构造的java程序的运行环,也是Java程序运行的环境,但是他同时一个操作系统的一个应用程序一个进程,因此他也有他自己的运行的生命周期,也有自己的代码和数据空间。JVM在整个jdk中处于最底层,负责于操作系统的交互,用来屏蔽操作系统环境,提供一个完整的Java运行环境,因此也就虚拟计算机。操作系统装入JVM是通过jdk中Java.exe来完成,通过下面4步来完成JVM环境:
1) 创建JVM装载环境和配置
2) 装载JVM.dll
3) 初始化JVM.dll并挂界到JNIENV(JNI调用接口)实例
4) 调用JNIEnv实例装载并处理class类。
1) JVM实例对应了一个独立运行的java程序它是进程级别
a) 启动。启动一个Java程序时,一个JVM实例就产生了,任何一个拥有public static void
main(String[] args)函数的class都可以作为JVM实例运行的起点
b) 运行。main()作为该程序初始线程的起点,任何其他线程均由该线程启动。JVM内部有两种线程:守护线程和非守护线程,main()属于非守护线程,守护线程通常由JVM自己使用,java程序也可以表明自己创建的线程是守护线程
c) 消亡。当程序中的所有非守护线程都终止时,JVM才退出;若安全管理器允许,程序也可以使用Runtime类或者System.exit()来退出
2) JVM执行引擎实例则对应了属于用户运行程序的线程它是线程级别的
JRE(JavaRuntimeEnvironment,Java运行环境),也就是Java平台。所有的Java 程序都要在JRE下才能运行。普通用户只需要运行已开发好的java程序,安装JRE即可。
JDK(Java Development Kit)是程序开发者用来来编译、调试java程序用的开发工具包。JDK的工具也是Java程序,也需要JRE才能运行。为了保持JDK的独立性和完整性,在JDK的安装过程中,JRE也是 安装的一部分。所以,在JDK的安装目录下有一个名为jre的目录,用于存放JRE文件。
JVM(JavaVirtualMachine,Java虚拟机)是JRE的一部分。它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。JVM有自己完善的硬件架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。Java语言最重要的特点就是跨平台运行。使用JVM就是为了支持与操作系统无关,实现跨平台。
第二部分介绍了JVM的组成部分,以下内容也会围绕部分二的图示来解说JVM的各个组成部分。
jvm介绍(很好的一篇文章)
http://wsqwsq000.iteye.com/blog/1120670
JVM介绍
http://www.cnblogs.com/549294286/p/3915691.html