一张图看懂JVM内存区域,清晰好记

运行时数据区域

Java虚拟机在执行Java程序时会把它所管理的内存分为若干个不同的数据区域,这些区域有各自的用途,以及创建时间和销毁时间,有的区域随着虚拟机进程的启动尔存在,有的区域则依赖用户线程的启动和结束尔创建和销毁。根据Java虚拟机规范,Java虚拟机所管理的内存将会包括以下几个运行时数据区。

程序计数器(Program Counter Register)

线程私有的一块较小的内存空间,当前线程所执行的字节码指令的行号指示器,字节码解释器工作时,就是通过改变这个指示器的值来选取下一条需要执行的字节码指令,分支,循环,跳转,异常处理,线程恢复等基础功能就是依赖这个计数器来完成。

虚拟机栈(Java Virtual Machins Stacks)

Java方法执行的内存模型,线程私有,生命周期与线程一致,每个方法在执行时都会创建一个栈桢(Stack Frame),栈桢是方法运行时的基础数据结构,用于存放局部变量表,操作数栈,动态链接,方法出口等信息,每一个方法从调用到执行完成的过程就对应着一个栈桢在虚拟机中入栈和出栈的过程。

本地方法栈

本地方法栈和虚拟机栈所发挥的作用基本相同,区别在于虚拟机栈是为虚拟机执行的Java方法服务的,但是本地方法栈是为本地方法(Native)服务的,在虚拟机规范中没有明确规定对这本地方法栈使用的语言,使用方式和数据结构并没有强制规定,因此具体的虚拟机可以自由的实现它,与虚拟机栈一样,本地方法栈也会抛出OutOfMemoryException和StackObverFlowErr

堆(Heap)

所有线程共享,随线程的启动而创建,存放对象实例,是垃圾回收器管理的主要区域(GC堆),从内存回收角度考虑,可分为新生代,老年代,再细致一点可分为Eden控件,From Survivor控件,ToSurvivor空间,从内存分配的角度看,线程共享的JAVA堆可划分为多个线程私有的分配缓冲区。

方法区(Method Area)

所有线程共享,存放已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据,Java虚拟机对这块的限制非常宽松,可以选择不实现垃圾回收,相对而言,垃圾收集行为在这个区域很少出现,这块区域的回收目的主要针对常量池的回收和对类型的卸载,这个区域的回收成绩很难令人满意,尤其是类型卸载,条件箱单苛刻,但是对这块区域的回收确实是有必要的。

运行是常量池

方法区的一部分,Class文件中除了有类的版本,方法,接口,字段等描述信息之外,还有一项信息是常量池,用以存放编译器生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。

直接内存

并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是也被频繁使用,JDK1.4中加入NIO类,引入一种基于通道和缓存的I/O方式,通过使用Native函数直接分配对外内存,然后通过存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景显著提高性能,避免在Java堆和Native堆中来回复制数据(Native堆:非Java堆,通过I/O方式与Java堆建立联系,避免数据来回复制)。

HotSpot虚拟机对象创建,布局,访问过程

对象的创建,布局,访问过程必须把讨论范围限定在具体的虚拟机和数据区域上才有意义,本节讨论以HotSpot虚拟机Java堆内存为例。

对象创建大致分为

1,检查类加载

2,执行类加载

3,对象内存分配

4,对象分配的内存空间初始化

5,对象属性设置

6,执行<init>方法初始化对象属性值

虚拟机遇到new指令是,会根据new指令的参数在常量池中找一个能定位一个类的符号引用,检查这个类是否已被加载,解析,初始化,如果没有则执行类加载,如果已加载,则执行第三步。对象内存分配有两种方式,一种为指针碰撞(分配一块完整的区域),一种为空闲列表(分配若干个不完整的小区域),具体使用哪一种分配方式有虚拟机垃圾回收器的算法决定,如果算法带内存整理功能,空闲内存空间连续,则会使用指针碰撞方式,反之。由于存在线程安全问题,虚拟机采用CAS配上失败重试来保证操作的原子性,另一种方式是把内存分配动作按照线程划分在不同内存空间进行,称为本地线程分配缓冲区。

接着将内存空间初始化为零值,然后设置对象是哪个类,如何找到元数据,哈希码,对象分代年龄等信息,最后执行<init>方法完成对象的创建。

对象的内存布局分为三个部分

1,对象头

2,实例数据

3,对齐填充

对象头中分为两部分信息:运行时数据和类型指针,运行时数据就是指前面提到的内存空间设置的内容,类型指针指向它的类元数据。实例数据是指对象的字段内容,包括从父类继承来的字段内容。对其填充不一定存在,没有实际意义,只是起到占位符的作用。

对象的访问定位方式目前主流的有两种

1,使用句柄

2,直接指针

使用句柄的方式是在栈的reference中指向Java堆中的句柄池中的某个句柄,句柄中记录对象实例数据和对象类型数据的实际位子信息,直接指针方式是直接在栈的reference中存放对象实例数据的地址,这种方式必须考虑如何在对象实例数据中存放对象类型数据的地址信息。两种方式各有优势,使用句柄优势在于对象被移动是只需要改句柄指向的数据地址,reference指向的句柄地址不会改变。直接指针的速度更快,节省了一次指针定位的时间开销(?)

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值