【JVM】运行时数据区域,内存如何分配和对象在内存中的组成

【JVM】运行时数据区域,内存如何分配和对象在内存中的组成_第1张图片

目录

一.运行时数据区域

1.线程独享

2.线程共享

二.内存如何分配

1.指针碰撞法

2.空闲列表法

3.TLAB

三.对象在内存中的组成

​编辑1.对象头

2.实例数据

3.对齐填充


一.运行时数据区域


1.线程独享

【JVM】运行时数据区域,内存如何分配和对象在内存中的组成_第2张图片
(1)栈
虚拟机栈:每个 Java 方法在执行的同时,会创建一个栈帧,用于存储局部变量表、操作数栈、常量池引用等信息;方法的调用过程,就是一个栈帧在 Java 虚拟机栈中入栈和出栈的过程;
本地方法栈:和虚拟机栈很类似,区别在于虚拟机栈为 Java 方法服务,本地方法栈为 Native 方法服务;其中 Native 方法可以看做用其它语言(C、C++ 或汇编语言等)编写的方法;
(2)程序计数器
      一个 CPU 在某个时间点,只能做一件事情,在多线程的情况下,CPU 运行时间被划分成若干个时间片,分配给各个线程执行;
程序计数器的作用就是记录当前线程执行的位置,当线程被切换回来的时候,能够找到该线程上次运行到哪儿了;所以程序计数器一定是线程隔离的。

2.线程共享

【JVM】运行时数据区域,内存如何分配和对象在内存中的组成_第3张图片
(1)方法区
方法区用于存放已被加载的类信息、常量、静态变量、即编译器编译后的代码等。
还有要注意的一点:方法区是 JVM 的规范,在 JDK 1.8 之前,方法区的实现是永久代;从 JDK 1.8 开始 JVM 移除了永久代,使用本地内存来存储元数据并称之为:元空间(Metaspace)。

(2)堆
对于堆栈的区别总结一句话:堆中存对象,栈中存基本数据类型和堆中对象的引用;一个对象的大小是可以动态变化的,而引用是固定大小的。
这么看就容易理解堆为什么是线程公有的了,省地儿啊

二.内存如何分配


1.指针碰撞法

【JVM】运行时数据区域,内存如何分配和对象在内存中的组成_第4张图片
适用于堆内存完整的情况,已分配的内存和空闲内存分表在不同的一侧,通过一个指针指向分界点,当需要分配内存时,把指针往空闲的一端移动与对象大小相等的距离即可,用于Serial和ParNew等不会产生内存碎片的垃圾收集器。

2.空闲列表法

【JVM】运行时数据区域,内存如何分配和对象在内存中的组成_第5张图片
适用于堆内存不完整的情况,已分配的内存和空闲内存相互交错,JVM通过维护一张内存列表记录可用的内存块信息,当分配内存时,从列表中找到一个足够大的内存块分配给对象实例,并更新列表上的记录,最常见的使用此方案的垃圾收集器就是CMS。

3.TLAB

【JVM】运行时数据区域,内存如何分配和对象在内存中的组成_第6张图片


三.对象在内存中的组成

【JVM】运行时数据区域,内存如何分配和对象在内存中的组成_第7张图片
1.对象头


(1)markword
记录了该对象锁相关的信息、分代年龄、hashCode,在32位JVM中占32bit,在64位JVM中占64bit

(2)指向类型的指针
指向方法区对应class信息的指针,在32位JVM中占32bit,在64位JVM中占64bit(开启指针压缩的情况下占32bit)

(3)如果是数组-》数组长度
如果是数组的话,组成中会包含数组长度,32bit

2.实例数据


类的实例信息

3.对齐填充


JVM要求Java对象的大小应该是8bit的倍数,这部分就是将对象大小补充为8bit的倍数
 

你可能感兴趣的:(jvm,java,开发语言,安全)