您的位置:首页 > 编程语言 > Java开发

Java内存区域-JVM

2016-03-31 14:53 387 查看
Java虚拟机运行时数据分为:

方法区,堆,

虚拟机栈,本地方法栈,程序计数器。



Java虚拟机栈,

是线程私有的,生命周期和线程相同,描述了Java的方法执行模型,每个方法执行时都会创建一个栈帧,会抛出StackOverFlowError和OOM。

本地方法栈

类似Java虚拟机栈,只是描述的是本地方法的执行模型。也会抛出StackOverFlowError和OOM。

Java堆

Java堆是所有线程共享的,在虚拟机启动的时候创建,存放对象的实例,Java堆也是垃圾收集器的主要区域,GC堆。

程序计数器

可理解为当前线程执行的字节码的指示器。对于多线程来说,每条线程都有自己的程序计数器,即各个线程之间的计数器互不影响,由于多线程完全有可能会出现线程中断,则当被中断的线程需要恢复执行的时候,就需要计数器来知道上次该线程执行到哪里了。

因为每个线程都有自己的计数器,当CPU重新调度该线程时,从计数器中取出下一条的字节码执行指令,就可以继续执行了。

对象的创建

首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用,且检查这个符号引用代表的类是否已被加载、解析和初始化。如果没有则继续执行。

虚拟机会为新生的对象分配内存,主要是两种内存分配策略,一是指针碰撞,一种是空闲列表。指针碰撞就是把Java堆中的内存分为两部分,一部分是所有用过的内存(不能被分配了),一部分是空闲的内存(可以被分配),可用和不可用的内存之间又一个分割点指示器,为对象分配内存实际就是从这个分割点指示器向空闲内存的那边移动一段空间。

而空闲列表则不同,空闲列表中已使用的内存和空闲内存有可能是交叉在一起的,对其使用指针碰撞的方式分配内存就会出现问题,虚拟机维护着一张列表,列表中记录了哪些内存是可用的,则分配内存的时候就从可容纳对象要求大小的内存区域中分配内存给这个对象。

虚拟机将分配到的内存空间都初始化为零,保证Java的一些原生数据类型在不重新赋值的时候就可以直接使用,程序在使用这些对象的时候可以直接使用零值。

虚拟机对对象进行一些必要的设置,可以知道这些对象是哪个类的实例,对象的哈希码,对象的GC分代年龄等信息,

接着进行new对象之后初始化,执行init方法,把对象进行初始化。

对象的内存布局

主要包括三部分:对象头,实例数据和对齐填充。

对象头包含两部分:一是用于存储对象自身运行时数据,比如哈希吗,GC分代,锁状态标识,线程持有锁,偏向线程ID,偏向时间戳等,二是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例,

实例数据部分是对象存储的有效信息,代码中所定义的各种类型的字段内容。

对齐填充是由于规定对象的起始地址须是8字节的整数倍,很可能上两个部分大小不够8个字节的整数倍,即进行填充。

对象的访问定位

创建一个对象,要使用对象须定位这个对象,主要通过Java栈中的reference数据,通过这个reference数据只是一个指向对象的引用,对象的访问方式可以不同。对象访问方式主要有句柄和直接指针两种。

句柄访问会在Java堆中划分出一块句柄池,存放了对象的实例数据和类型指针。而reference数据则存放句柄的地址引用,使用直接指针访问对象,则reference数据存放的就是对象的地址。

使用句柄访问的好处是reference中存储的稳定的句柄地址,当对象的地址发生改变可以不用关心。

而直接指针的好处是速度更快,节省了一次指针定位的时间。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: