java虚拟机内存 对象创建引用 概述

原创
2018/08/20 23:22
阅读数 68

1、Java虚拟机运行时数据分区图

程序计数器:线程私有,是一块较小的内存空间,它是当前线程所执行的字节码文件的行号指示器

java虚拟机栈:线程私有,其生命周期与线程相同,这也就是我们平时所粗略分的堆/栈中的栈。虚拟机栈描述的是java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接方法出口等信息。每个方法从调用直至执行完成的过程,就对应着一个栈桢在虚拟机栈中入栈到出栈的过程。局部变量表所需要的内存空间在编译期间便完成了分配,当进入一个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。

本地方法栈:与虚拟机栈作用相似,他们的区别是虚拟机栈是为虚拟机执行java方法(字节码)服务,本地方法栈则是为虚拟机使用native方法服务。虚拟机规范中对本地方法栈中方法使用的语言、方式和数据结构没有限制,因此不同的虚拟机可以自由的实现。而且有的虚拟机会直接把本地方法栈和虚拟机栈合二为一(如Sun HotSpot)

Java堆(Java Heap):堆是java虚拟机所管理的内存中最大的一部分,java堆是被所有线程共享的一块内存区域,在虚拟机启动的时候创建,其存在的目的就是存放对象实例,几乎所有的对象实例和数组都会在堆上分配内存。但是随着JIT编译器与逃逸分析技术的逐渐成熟,栈上分配,标量替换优化技术将会导致一些变化,所有对象都在堆上分配也不再那么“绝对”

方法区:所有线程共享,用于存储已经被虚拟机加载的类信息,常量,静态变量,即使编译器编译后的代码等数据,虽然java虚拟机把它描述为堆的一个逻辑部分,但是其有个别名就做非堆(Non-Heap)。

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

直接内存:直接内存并不是Java虚拟机运行时数据区域的一部分,也不是java虚拟机规范中定义的内存区域。但是这部分内存也会被平凡的使用,而且一样会发生OOM。JDK1.4中加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的IO方式,可以直接使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer的对象作为这块内存的引用进行操作,这样能在一些场景中显著提高性能,因为避免了从java堆和Native堆中复制数据。直接内存的分配不受java堆大小限制,但是肯定会受到机器总内存(RAM,SWAP,或者分页文件)和处理器寻址空间的限制,毕竟它也是内存。

2、对象的创建

在java语言中创建对象是通过new关键字来进行的,这里所说的是当虚拟机遇到new指令后是怎么样来创建一个对象的。这里仅仅讨论一般对象的创建(不包括数组和Class对象)

当虚拟机遇到new指令后,首先回去检查这个指令的参数是否能够在常量池中定位到一个类的符号引用,并去检查这个符号引用代表的类是否已经被加载、解析和初始化过,如果没有就必须先执行相应的类加载。

类加载检查通过,虚拟机会为新的对象分配内存,因此对象所需内存的大小将会在类加载完毕后被完全确定,为对象分配空间的过程就是将一块大小确定的内存从java堆中划分出来。

3、对象的定位

那么对于创建的对象虚拟机是怎么在访问的呢?

java程序需要通过java虚拟机栈中保存的reference数据来定位和操作java堆中的对象。由于reference在java虚拟机规范中只是规定了一个指向对象的引用,并没有定义这个引用是如何去定位和访问堆中的对象的具体位置,因此对象的访问方式也取决于虚拟机的具体实现。

目前主流的访问方式是:1)使用句柄;2)使用直接指针

句柄访问:java堆中将划分出一块内存区域作为句柄池,reference存储的则是对象的句柄地址,句柄中包含了对象的实例数据和类型数据,已经地址等信息

直接指针:reference中存储的直接就是对象在java堆中的内存地址值。

使用这两种方式各有优劣:

句柄访问最大的好处是reference中存储的句柄地址是稳定不变的,对象被移动后改变的只是句柄中的地址,reference本身是不需要改变的。

使用直接指针速度快,可以根据reference中存储的内存地址值直接找到对应的对象,避免了一次指针定位的时间开销,java中对对象的访问时非常频繁的,因此这部分开销是非常可观的。

java中常讲的基本上也是通过直接指针的方式来访问对象的,也就是栈中保存的reference记录堆中的对象的内存地址值,通过内存地址值来访问堆中的对象

参考:《深入理解Java虚拟机》

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部