实验简介
Java技术体系中所提倡的自动内存管理最终可以归结为自动化地解决了两个问题:给对象分配内存以及回收分配给对象的内存。关于回收内存这一点,我们会在下面的内容去介绍虚拟机中的垃圾收集算法,现在我们来探讨一下给对象分配内存的那点事儿。
预期效果
知道JVM对象分代,了解虚拟机优化。
实验目的
1.了解对象在各个年代的状态。
2.了解垃圾回收机制。
实验流程
JVM会怎么分配内存?大多数人的回答是内存分为堆和栈,但实际上内存的分配比堆栈更严格
内存被划分为5个大区:程序计数器、本地方法栈、虚拟机栈、堆、方法区。
程序计数器(线程独享)当前线程所执行的字节码的行号指令器,通过改变计数器的值选取虚拟机下一条需要执行的字节码指令,分支、循环、跳转、异常、线程恢复等基础功能。
虚拟机栈(线程独享)JAVA方法执行的内存模型,每个方法在执行的同时都会存建一个栈帧用于存储方法出口、局部变量表、操作数栈、动态连接等信息,局部表量表(大多数人认为的栈)用于存放可知各种数据类型(Boolean、byte、char、short、int、float、long、double)、对象引用类型和returnAddress(指向了一个字节码指令地址)
本地方法栈与虚拟机栈的作用非常相似,它们之间的区别是虚拟机栈为虚拟机执行JAVA方法服务,本地方法栈为Native方法服务。
方法区(线程共享)被虚拟机加载的常量、静态量、方法描述、类信息等。JAVA虚拟机规范把方法区描述为堆的一个逻辑部分,但它还有一个别名叫做Non-Heep 运行时常量池:方法区的一部分,用于存放编译时的字面量、符号引用。
堆(线程共享)在分配对象时,对象包括了三部分内容:对象头(存储自身运行数据、类型指针)、实例数据(对象真正有意义的数据)、对齐填充(并不是所有的JVM都有,以0填充)。
对象头中用于存储自身的运行数据部分:如哈希码、GC分代年龄、锁状态标志、线程持有锁、偏向线程ID、偏向时间戳等,官方称为Mark Word Mark Word 32位空间中的25位用于存储HashCode, 4位存储对象分代年龄,2位用于锁标志位,1位固定0
对象头中用于存储类型指针部分:即对象指向的类元数据指针,虚拟机通过这个指针来确定是这个对象是哪个类的实例。如果对象是一个JAVA数组,那在对象头中还必须有一块用于记录数组长度的数据,虚拟机可以通过普通JAVA对象的元数据确定JAVA对象大小,但是从数组的元数据中无法确定数组大小
实例数据:对象真正有效信息,也是程序代码中所定义的各种类型的字段内容。无论从父类继承还是子类定义的,都需要纪录。这部分的存储顺序受虚拟机分配策略影响,部分虚拟机分配为longs/doubles、ints、shorts/chars、bytes/booleans、oops(Ordinary Object Pointers)可以看出。相同宽度总是被分配到一起。
那么对象是如何被访问的?AVA程序需要通过栈上的reference数据来操作堆上的具体对象。由于reference类型在JAVA虚拟机中只规定了一个指向对象的引用,并没有规定如何访问,所以对像的访问方式取决于虚拟机的实现而定,访问的方式大体分为使用句柄访问和使用直接指针访问。
使用句柄访问:JAVA堆中将会划分出一块内存作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体信息。

使用直接指针访问: JAVA堆对象的存局中就必须考虑如何放置访问数据的相关信息,而reference中存储的直接就是对象地址。

JVM中对象的分配