运行时数据区
JVM(Java虚拟机)<b>运行时数据区</b>是指JVM在运行Java程序时所创建的<b>内存区域</b>,主要用于存储程序<b>运行时所需要的数据</b>。
灰色线程共享数据区,白色线程隔离数据区
程序计数器
一块较小的内存空间,可以看做是<b>当前线程</b>所执行的<b>字节码</b>的<font color="#f44336"><b>行号指示器</b></font>。
<font color="#e74f4c">字节码解释器</font>,就是<b>通过改变该内存空间内容</b>,来选取下一条需要执行的字节码指令
是程序<font color="#f44336"><b>控制流的指示器</b></font>,例如 分支、循环、跳转、异常处理、线程恢复
线程恢复是指,多线程条件下,<font color="#f44336"><b>线程切换后,需要知道之前执行字节码指令的位置,每个线程私有</b></font>
如果执行的是Java方法,计数器存储<b>正在执行<font color="#ff0000">字节码指令的地址</font></b>,如果是Native方法,则为空Undified
虚拟机栈
线程私有,生命周期和线程相同
<b>每个方法被执行的时候</b>,Java虚拟机都会同步创建一个<b><font color="#f44336">栈帧</font></b>
<b><font color="#ff0000">局部</font>变量表</b>
存放<b><font color="#f44336">编译期可知的</font></b>各种<b>基本数据类型</b>、<b>对象引用</b>、<b>returnAdress类型</b>(指向下一条字节码的地址)
以上类型数据在变量表中的存储空间以<b>局部变量槽(Slot)</b>来表示,其中64位长度的long和double类型的数据都<b>只会占用两个变量槽,其余占用1个</b>
局部变量表内存空间<b>在<font color="#e74f4c">编译期间</font>大小完全确定</b>,<b>运行期间不会改变</b>局部变量表的大小
<b>操作数栈</b>
动态链接
方法出口
每个<b>方法</b>被调用到执行完毕,都对应一个<b>栈帧</b>在虚拟机栈中<b>从入栈到出栈的过程</b>
如果栈的深度>虚拟机所允许深度
StackOverFlowError
虚拟机栈允许扩容发,栈扩展无法申请到足够内存
OutOfMemoryError
本地方法栈
与虚拟机栈很相似,<b>为本地方法<font color="#e74f4c">Native方法</font></b>服务的
<b>HotSpot虚拟机</b>直接将<font color="#e74f4c"><b>Java虚拟机栈和本地方法栈合二为一</b></font>
Java堆
线程共享,存放<b><font color="#f44336">对象实例</font></b>
对象
对象内存布局中的<b>实例数据</b>存放<b>成员变量</b>
数组
垃圾回收器管理的区域范围,也被称为<b>GC堆</b>
Java<b>堆</b>的划分
<b><font color="#ff0000">分代收集</font>理论</b>划分
新生代Young(Coping) 1
Eden 8
用于存放<b>新创建</b>的Java对象,大多数对象都在这里被分配。当Eden区内存满时,触发<b>Minor GC</b>(新生代垃圾回收),将不再使用的对象回收掉。
survivor1 1
survivor2 1
老年代Old(MS MC) 2
用于存放<b>长时间存活的对象</b>,例如服务器应用程序的缓存数据、对象池等。当老年代内存满时,触发<b>Major GC</b>(老年代垃圾回收),一般采用标记-清除(Mark-Sweep)算法或标记-整理(Mark-Compact)算法。
TLAB
Thread Local Allocation Buffer
解决内存争用问题
共享堆划分出多个<b>线程私有的分配缓冲区</b>
Java堆<b>物理上可以不连续</b>,但是<b>逻辑上应该视为连续</b>
Java堆可以实现成固定大小,也可以是可扩展的,主流Java虚拟机都是<font color="#f44336"><b>按照可扩展来实现的</b></font>
Java堆中<b>没有内存完成实例分配</b>,并且<b><font color="#f44336">堆也无法再扩展时</font></b>
OutOfMemory
方法区
线程共享区域,存放已被虚拟机<b>加载</b>的
<b>类型信息(静态数据结构-->动态数据结构 也就是class对象)</b>
对象头中的class pointer 指向
<b><font color="#ff0000">静态</font>变量</b>
<font color="#ff0000"><b>JIT编译后的代码缓存</b></font>
<font color="#e74f4c"><b>运行时常量池</b></font>
方法区的一部分
class文件中<b>常量池表</b>,存放<b>编译期间的</b>
字面量
符号引用
永久代
直接内存
NIO,使用Native函数,直接分配<b>堆外内存</b>,然后通过存储在Java堆中的<b><font color="#f44336">DirectByteBuffer</font></b>对象作为这块区域的引用(<font color="#ff0000">虚引用</font>)进行操作,避免了<b>Java堆和Native堆中来回复制数据</b>
<font color="#ff0000">对象的创建</font>
执行<b>字节码new操作</b>时,<b>检查指令参数</b>,是否能在<b>常量池中</b>定位到一个类的<b><font color="#e74f4c">符号引用</font></b>,并检查这个引用代表的<b>类是否被 <font color="#ff0000">加载 连接 和 初始化</font> </b>过,类加载检查通过后↓
<b>为新生对象<font color="#ff0000">分配内存</font></b>,<b>对象所需内存在<font color="#ff0000">类加载后完全确定</font></b>
<font color="#ff0000">分配内存方式</font>
<b>指针碰撞</b>
内存空间绝对规整,直接移动新生对象大小空间
是否能用,取决于<b><font color="#ff0000">GC是否采用空间压缩整理</font></b>
<b>空闲列表</b>
内存空间不规整,虚拟机必须维护一个<b>空闲列表</b>法来进行内存分配
多线程下的内存分配问题
给A、B同时分配内存,可能存在<font color="#f44336">内存争用</font>问题
<font color="#ff0000">锁</font>
<font color="#f44336">CAS自旋锁</font>解决
<font color="#ff0000">TLAB</font>
<b>Java堆</b>预分配<b>线程私有的内存</b>,在该内存中进行分配
<b>内存初始化</b>
分配完内存,<b>默认初始化</b>,如果使用TLAB,直接在TLAB中完成,确保对象<b><font color="#e74f4c">实例字段在Java代码中可以不赋值,直接使用</font></b>
<b>对象必要信息的设置(</b>对象头<b>)</b>
markword
对象的<b>哈希码</b>
对象的<b>GC分代年龄</b>
class pointer
<b>对象类型</b>
类的元数据信息
new关键字后执行 <b><font color="#ff0000"><init>()方法</font></b>,按照<b><font color="#e74f4c">程序员的意愿</font></b>进<b>行对象初始化</b>
对象的<b><font color="#ff0000">内存布局</font></b>
对象头(Object Header)
<b>Mark word</b>
对象自身的运行时数据
HashCode
GC分代年龄
锁状态标志
<b>偏向线程ID</b>
<b>线程持有的锁</b>
偏向时间戳
未开启压缩指针,大小为32位虚拟机为4B 64位虚拟机为8B
是一个<b><font color="#ff0000">动态定义的数据结构</font></b>
<b>class pointer</b>
指向<b>类型</b>元数据的指针,通过其确认<b>对象类型</b>
<font color="#f44336"><b>实例数据(Instance Data)</b></font>
<font color="#ff0000"><b>实例字段</b></font>内容数据,无论是父类继承,还是子类定义的
对齐填充(Padding)
Hotspot虚拟机自动内存管理,要求对象其实地址必须是<b><font color="#ff0000">8的整数倍</font></b>,用于填充对齐