JAVA内存区域

##一、运行时数据区
     java运行时数据区能够分为如下几个部分,程序计数器,虚拟机栈,本地方法栈,堆,和堆中的方法区。以下图所示:
这里写图片描述
其中程序计数器、本地方法栈和虚拟机栈是线程私有的,方法区和堆是线程共享的,其中方法区是堆的逻辑组成部分。

###1.一、程序计数器
     java中的指令都是用一个字节表示的,因此叫字节码指令。**程序计数器指向当前线程字节码指令执行的行号,**特殊状况,若是正在执行的是java方法,那么是字节码的行号,若是是native方法,那么程序计数器为空。

###1.二、虚拟机栈
     介绍虚拟机栈以前先对栈帧作个介绍,**栈帧是虚拟机栈的组成元素,用来支持虚拟机方法调用和方法执行的数据结构,**一个方法包含使用一个栈帧,栈帧中包含了四个主要组成部分,**局部变量表,操做数栈,动态连接,方法出口。**结构图以下:
这里写图片描述
**局部变量表中包含了方法中须要用到的变量,在java程序编译成class文件的过程当中就已经肯定了哪一个方法有多少个局部变量,局部变量表的容量以slot为最小单位,**通常状况下一个slot是32位的,可以存放32位及如下的变量,**对局部变量表的访问时经过索引的方式访问的,**索引值的范围是0到slot的最大的数量-1,若是执行的是实例方法(非static),那么编号0表明的就是this,当前对象的引用,其他按照方法总变量的顺序进行序号的排序。这里作一个局部变量表的一般结构描述图:
这里写图片描述
对于这个方法在局部变量的中的存储结构以下:
这里写图片描述
而后是操做数栈,操做数栈的深度也在编译成class文件的时候肯定了,**用来存放方法中指令写入的数据,**字节码指令往操做数栈中进行写入和读取操做也叫作入栈和出栈。这里仍然用上面一个代码来画出程序运行过程当中程序计数器,操做数栈和局部变量表的关系。
这里写图片描述
而后是动态链接,动态链接是方法中调用其余方法是经过对对应方法的引用的调用实现的,持有方法的引用并经过这个引用调用对应的方法这个过程称为动态链接。在类加载中类中的方法会被存入方法区,方法的引用也在方法区中,在运行过程当中,栈帧会从方法区中获取对应方法的引用并对方法进行调用。
最后是方法返回地址,当一个方法开始执行后,若是调用了其余的方法,会将此时的程序计数器的地址也就是方法的返回地址给保存起来,从而再方法调用完毕后可以回到调用方法的位置,若是是正常退出,会有有返回值返回给调用者,若是是异常退出则不会,当程序退出时可能进行的操做有:恢复上层方法的局部变量表和操做数栈,将返回值压如操做数栈中,程序计数器指向下一条指令。

###1.三、本地方法栈
     本地方法栈和虚拟机栈的数据 结构基本是同样的,只是上面的存储的是java方法的数据,这里存储的是native方法的数据。

###1.四、方法区
     方法区是堆的一个逻辑区域,用来存放类的描述信息、静态变量、常量、编译后的代码等。首先是最可以理解的编译后的代码,也就是所说的class文件,由于在虚拟机进行类加载时候的第一步就会将class文件以字节流的方式进行加载,当进行了class文件格式验证可用后就会存储到方法区,因此这就是编译后的代码部分,而后咱们来看class文件的组成部分,就能理解为何这个区域还存放了类信息、静态变量、常量等数据了。class文件时jvm虚拟机可以解释执行或者编译执行的文件,是一组以字节为基础单位的文件,class文件采用相似于C语言中结构体相似的结构来存储数据,**结构体中只有两种数据类型,无符号数和表,且数据之间没有间隔,**全是数据,无符号数由u一、u二、u四、u8组成,表由无符号数和表组成。**这是文件的数据格式。接下来是文件的组成部分:**前面四个字节是魔数用于进行身份识别,肯定是不是class文件,**接着是4个字节 是版本号,**肯定是不是虚拟机支持的版本,**接下来是常量池,**常量池存放了字面量和符号引用,其中字面量是各类常量如字符串、常量值、类名、接口名、字段名、方法名等,符号引用是字段和方法以及类的描述符,经过符号引用能够定位到这些类,方法,和字段。**常量池过了是访问标志,**用来标志类或者接口的访问信息,如是不是public类,是否声明为final等等。**访问标志过了是类索引、父类索引、接口索引集合,**这几个是用来肯定类有哪一个父类和实现了哪些接口。**索引集合过了就是字段表集合,**这个用来描述类中声明的变量,注意这里是描述声明变量的各类信息。最后是方法表集合,同上面同样,这个是用来描述类中定义的方法的各类信息。还有一个就是属性表集合,这个属性表集合不是一个单独的区域而是字段表、方法表都包含的集合,用来描述某些场景专有的信息,例如方法表中的Code属性就存储了方法中java代码被编译事后的字节码指令,以及局部变量表的大小和操做数栈的深度等信息。因此看到这里就基本上知道为何方法区中存放了类信息、静态变量、常量等信息了,由于class文件被加载进了方法区,而这些东西都是class文件中所包含的东西。

###1.五、堆
     前面几个区域介绍完了,最后就这个对区域,也是最简单的区域,主要就放了java中的对象实例。

##二、虚拟机对象
     前面对java运行时数据区进行了详细的介绍,在介绍的过程当中基本也对每一个区域中数据如何存放进行了介绍,可是堆上对象的建立还没介绍,因此这里按如下三点进行介绍。

###2.一、对象的建立
     对象的建立主要要有一下几个步骤,**首先看能不能经过指令的参数在常量池中找到对应类的符号引用,****而后经过符号引用找到类后再判断这个类是否被加载、链接和初始化了,若是这几步有没完成的步骤,那先完成对应的步骤。而后在堆中给对象分配空间,分配内存能够采用CAS操做或者预先给线程分配内存缓冲池的方法,这个内存分配不会冲突,而后在对对象中的值进行初始化,**最后再对对象头进行设置,如hashcode、偏向锁、元数据指针等信息进行设置。

###2.二、对象的内存布局
     对象在内存中的存储是8字节对齐的也就是说对象存放的地址是8的倍数,由三部分组成,对象头,对象实例,对齐填充。其中对象头包含两个字或者三个字,在个人锁的博客中对对象头有详细的介绍,这里不作详细介绍,一个MarkWord用来存储对象的hashcode,分代年龄,锁信息等,一个用来存放对象元数据的指针。对象实例就是对象里面元素的存放,对齐填充就是使8字节对齐的,能够不要。

###2.三、对象的访问定位
     对象的访问定位分为两种,一种是直接访问,引用就是对象的地址,另外一种是句柄访问,引用是句柄的地址,而句柄存放了对象的地址,前者的好处是速度更快,效率更高,后者是对象被移动时,引用的地址不用变。通常经过第一种方法进行访问。
java