世界上有一半以上的程序员在使用JAVA编程,然后有一般以上的服务都采用JAVA虚拟机来运行,然后,我们真正对它了解有多少,多数人在编码的过程中可能都会忽视这个问题,当然也包括我,做了这么多年的程序员,写了也不少代码,但问题是,回想起来个人对其理解的程度也不咋的,所以今天利用这个时间来对JVM简单的描述下,我们既然要了解JAVA虚拟机,当然我们首先应该去了解它的体系结构,以及运行原理,体系结构的话,我在这里就不画图了,网上有很多,在这里,我也CP一张吧,这下面的图是正确的:
![](https://img-my.csdn.net/uploads/201206/06/1338967218_6098.gif)
从总体结构上看,上面可大体分为五部分来体现,第一部分是类装载子系统,第二部分是运行数据区,第三部分是执行引擎,第四部分是本地方法接口,第五部分就是本地方法库。OK,这是总体结构的划分,细分每个结构吧:
第一部分:类装载子系统,主要的工作就是为加载类文件,负责把相关的类加载进来
第二部分:运行数据区,可能这也是JVM里包括的内容最多也最复杂的部分,为什么呢,我们细下来看图,图中分为五小部分,方法区,JAVA栈,JAVA堆,程序计数器,本地方法栈。
好,现在我们来详细分解下这个所为最复杂的部分,首先我们来理解,我们为什么需要这个方法区的存在,想想方法区的字面含义理解应该是信息存储的展示区吧,不知道我的理解对不对,也就是说在我们通过类装载子系统把相关的类装载进运行数据区时,其类会经过一个编译器编译的过程,当编译器编译后会出现一些相关的信息,如属性,常量,方法等,然而这些信息进入运行数据区后存放位置如何呢,首先它们考虑了方法区,毕竟方法去就是用来存放其相关信息的,然而此时并可能不会有任何堆栈内存的分配,只是还需要进一步去走流程与分解,这时一旦信息进入方法去后,想想,检查下是否有相关的指令集的声明,如JAVA的基本数据类型,指令代码,以及常量等,若有就要求JAVA栈分配其定长空间来存储,在这里说到栈了,我们来理解下栈,什么是JAVA栈呢,简单的来说,JAVA栈其实就是一个定长内存指令存储区域,它的优点是什么,优点有,存储速度快,对内存管理简单,对指令集内存分配为定长分配,而且是按顺序存储的,即以压栈与弹栈的方式来进行简单操作,还有没有别的优点呢,先别急,等我们看来JAVA堆对比以下就知道了,前面说到类装载后编译的信息首先存储在方法区,其次以选定的方式进行对栈作来相应分配,当然,这时候还会作另一个检测,就是该类是否有对其实例对象,若有,不好意识,实例对象对内存分配是属于动态改变的,想要栈来支持,没门,栈不答应,因为那样违反了栈的存储原则,没办法就只能去找JAVA堆了,因为听说只有JAVA堆才支持内存动态分配,OK,JAVA堆答应了,但是问题来,你这个实例对象咋就那么千变万化呢,你的数据量总是在变化,而且有时候我需要给你分配很大的内存空间,甚至你不用了,你还栈着的可能性都有,不行,这是堆得想办法对其实例对象进行检查,要是我给其分配的内存空间,它不用了,咋就给它收回来,这样也可以免得过分浪费资源嘛,这时候堆就整了个GC(垃圾回收)机制,GC主要是来监控其在堆中实例的对象是否一直占用其内存,若没有,就对其收回,想到这里,在堆中实例的都是动态数据啊,没有相应的标记来检查,而且堆中的内存是属于其所有线程所共享,这可怎么办,GC后面想了个办法,栈不是存放指令的地方吗,而且管理与操作都很简单,又效力,想想,可以这样做,每到堆栈中实例的对象,都需要在栈中存储一个执行堆中实例对象的4字节指令码,它们是一一对应的,这样,我就可以通过在栈中的指令集来很快速与方便的查找到在堆中实例的对象数据了,OK,问题解决了,相对于说,在栈中的指令指向堆中的的实例,这个指令就好比是一个地址指针,就类似于一个信封地址一样,在这里我们也就把堆给顺便讲解了,在细一点吧,在JAVA编码中,我们长会用static这个修饰符,在JAVA中也就是所为的静态与非静态的区别,然而我们怎么去区分它们呢,这可能需要我们去结合JAVA中的堆栈来进行讲解,大家都知道,静态方法是可以直接调用的,而非静态方法是需要实例才能访问的,在我们细下里看DALVIK的源码时,都会发现在需要实例的非静态方法时会有一个默认的隐含参数的操作,当然这个隐含参数不是自己给的,是由JVM来分配的,没实例一个非静态方法,JVM都会给该实例在STACK中给出一个默认的隐含参数指令来指向堆中实例化了的对象,这样就可以通过这个指令来寻找到在对象动态实例的对象了,当然,这里我们可能还会牵涉到程序计数器,它做什么用的呢,它主要是对其字节码的行号作指示器,也就是说,在一个class文件被load进虚拟机后,方法指令保存在栈中,堆中此刻是没有数据的,此时程序计数器开始执行指令,如果是静态方法就直接执行指令代码,此时指令代码是不能访问堆中的数据区域的,如果是非静态方法,由于隐含参数没有值,也就无法通过栈中的指令去检查到堆中的数据,也就会报错,这时候必须对其非静态方法进行实例才是,然后把在栈中分配的指令给非静态方法,这样程序计数器就依次执行指令就检查到堆中实例的相应数据区域了。在这里,我们需要理解下静态方法与动态属性,然动态属性需要栈中的指令才能发现去堆中实例对象数据区域,在这里我们不难知道,静态属性可能也是放在栈中存储了,这样定长的数据很容易算出偏移量,为此不管类的什么方法,都可以访问到类的静态属性,此就成来全局属性的称谓,当然实例对象的方法指令也是存储到栈中的,这点得稍微注意以下。剩下的就是本地方法栈了,它我们又该怎么去理解呢,其实这个本地方法栈很好理解,简单的描述就是服务于本地方法的通信方式。
好了上面是对运行数据区的理解,还有执行引擎,与本地方法接口和本地方法库,这几个问题,我就不详细讲解,因为它们存在的理由很简单就是一个适配过程。
介绍完以上,我们就结合之前讲解的策略模式来对其简单的描述下吧,当然我的理解并不一定正确,只是拿来作参考罢了,前面我们讲解的策略模式是以锦囊妙计的话题来展开讲解的,用在这里也无妨,类似于JVM的运行数据区就是一个锦囊,它包括了很多妙计的执行,只是一般的妙计的是独立执行的,但最终都有一个目的就是去完成这总的过程才是,如在锦囊妙计中,目的就是让刘备去江东安全的把老婆娶回来,然JVM中的过程目的就是把装载的类稳定的走起来,并获得结果,类似于方法区,程序计数器,栈,堆,本地方法栈等都是其妙计之一,只是功能表现不同罢了,OK,在执行引擎相对来说就是赵云,二类装载器好像就是诸葛亮(这个比喻可能不太好),其本地方法接口就类似于执行操作规则,OK,这样比喻不知道正确否,说了这么多,唯一想表达的是,我们在编码过程中不仅仅只是知道代码可以编出来就行了,还要去理解它为什么要这么编,这也算是我多年来的经验总结吧,尤其是在JAVA中我们可能太少去考虑内存分配与算法优化等问题,我这些在以后我的程序生涯中需要随时警惕才是,因为我一生的梦想就是做一个真正合格的软件工程师,二不仅仅只是一个会写代码的程序员,我想我们都应该是,在下节,我顺便讲解下JVM的GC吧,