深入理解java虚拟机笔记Chapter2
java虚拟机运行时数据区
首先获取一个直观的认识:

程序计数器
- 线程私有。各条线程之间计数器互不影响,独立存储。
- 当前线程所执行的字节码行号指示器。字节码解释器工作时通过改变这个计数器值选取下一条需要执行的字节码指令(分支、循环、跳转、异常处理都需要依赖此计数器)。
- 多线程运行时通过此计数器在线程切换后恢复正确执行位置。
- 是 5 个区域中唯一不会出现 OOM 的区域。
- 执行 Java 方法和 native 方法时的区别:
- 执行 Java 方法时:记录虚拟机正在执行的字节码指令地址;
- 执行 native 方法时:无定义;
Java 虚拟机栈
- 线程私有。
- 方法执行的内存模型。方法执行时创建栈帧(局部变量表、操作数栈、动态链接、方法出口等)。换一种说法,Java 方法执行的内存模型,每个方法执行的过程,就是它所对应的栈帧在虚拟机栈中入栈到出栈的过程。
- 局部变量表存放编译期可知的基本数据类型、对象引用和returnAddress类型。其中long, double占用2个局部变量空间(slot),其余占用1个slot。
- 局部变量所需内存空间编译期完成分配,运行期不改变其大小。
- 虚拟机参数设置:
-Xss. - 可能抛出的异常:
- OutOfMemoryError(在虚拟机栈可以动态扩展的情况下,扩展时无法申请到足够的内存);
- StackOverflowError(线程请求的栈深度 > 虚拟机所允许的深度);
本地方法栈
- 服务于 native 方法;
- 可能抛出的异常:与 Java 虚拟机栈一样。
Java 堆
- 所有线程共享,虚拟机启动时创建。
- 又称GC堆,垃圾收集管理的主要区域。
- 可以处于物理上不连续的内存空间中;
- 可能抛出的异常:OutOfMemoryError(堆中没有内存可以分配给新创建的实例,并且堆也无法再继续扩展了)。
- 虚拟机参数设置:
- 最大值:
-Xmx - 最小值:
-Xms - 两个参数设置成相同的值可避免堆自动扩展。
- 最大值:
方法区
- 所有线程共享。
- 存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据;
- 类信息:即 Class 类,如类名、访问修饰符、常量池、字段描述、方法描述等。
- 垃圾收集行为在此区域很少发生;不过也不能不清理,对于经常动态生成大量 Class 的应用,如 Spring 等,需要特别注意类的回收状况。
- 运行时常量池也是方法区的一部分.
- 可能抛出的异常:OutOfMemoryError(方法区无法满足内存分配需求时)。
运行时常量池
- 方法区的一部分。
- Class文件除了有类的版本、字段、方法、接口等描述信息,还有一项信息就是常量池,用于存放编译时期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池。
- 动态性。运行期也可放入新的常量。eg.
String.intern()方法。
直接内存
- NIO:可以使用Native函数库直接分配堆外内存。避免Java堆和Native堆来回复制数据。
- 虚拟机参数设置:-XX:MaxDirectMemorySize
- 默认等于 Java 堆最大值,即
-Xmx指定的值。
- 默认等于 Java 堆最大值,即
- 将直接内存放在这里讲解的原因是它也可能会出现 OutOfMemoryError;
- 服务器管理员在配置 JVM 参数时,会根据机器的实际内存设置
-Xmx等信息,但经常会忽略直接内存(默认等于 -Xmx 设置值),这可能会使得各个内存区域的总和大于物理内存限制,从而导致动态扩展时出现 OOM。
HotSpot虚拟机对象
对象的创建(遇到一条 new 指令时)
- 检查这个指令的参数能否在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,先把这个类加载进内存;
- 类加载检查通过后,虚拟机将为新对象分配内存,此时已经可以确定存储这个对象所需的内存大小;
- 在堆中为新对象分配可用内存;
- 将分配到的内存初始化;
- 设置对象头中的数据;
- 此时,从虚拟机的角度看,对象已经创建好了,但从 Java 程序的角度看,对象创建才刚刚开始,构造函数还没有执行。
第 3 步,在堆中为新对象分配可用内存时,会涉及到以下两个问题:
如何在堆中为新对象划分可用的内存?
- 指针碰撞(内存分配规整)
- 用过的内存放一边,没用过的内存放一边,中间用一个指针分隔;
- 分配内存的过程就是将指针向没用过的内存那边移动所需的长度;
- 空闲列表(内存分配不规整)
- 维护一个列表,记录哪些内存块是可用的;
- 分配内存时,从列表上选取一块足够大的空间分给对象,并更新列表上的记录;
如何处理多线程创建对象时,划分内存的指针的同步问题?
- 对分配内存空间的动作进行同步处理(CAS);
- 把内存分配动作按照线程划分在不同的空间之中进行;
- 每个线程在 Java 堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB);
- 哪个线程要分配内存就在哪个线程的 TLAB 上分配,TLAB 用完需要分配新的 TLAB 时,才需要同步锁定;
- 通过 -XX:+/-UseTLAB 参数设定是否使用 TLAB。
用图表示:

对象的内存分布
对象头
- 第一部分:存储对象自身运行时的数据,HashCode、GC分代年龄等(Mark Word);
- 第二部分:类型指针,指向它的类元数据的指针,虚拟机通过这个指针来判断这个对象是哪个类的实例(HotSpot 采用的是直接指针的方式访问对象的);
- 如果是个数组对象,对象头中还有一块用于记录数组长度的数据。
- 由于对象需要存储的运行时数据很多,考虑到虚拟机的内存使用,markOop被设计成一个非固定的数据结构,以便在极小的空间存储尽量多的数据,根据对象的状态复用自己的存储空间.
实例数据:
- 对象真正存储的有效信息。
对齐填充
- 仅起占位符的作用。HotSpot VM要求对象起始地址必须是8字节整数倍,换句话说,就是对象的大小必须是8字节的整数倍。当实例数据部分没有对齐时,通过对齐填充来补全。
对象的访问
Java 程序需要通过虚拟机栈上的 reference 数据来操作堆上的具体对象,reference 数据是一个指向对象的引用,不过如何通过这个引用定位到具体的对象,目前主要有以下两种访问方式:句柄访问和直接指针访问。
句柄访问
句柄访问会在 Java 堆中划分一块内存作为句柄池,每一个句柄存放着到对象实例数据和对象类型数据的指针。
优势:对象移动的时候(这在垃圾回收时十分常见)只需改变句柄池中对象实例数据的指针,不需要修改reference本身。

直接指针访问
直接指针访问方式在 Java 堆对象的实例数据中存放了一个指向对象类型数据的指针,在 HotSpot 中,这个指针会被存放在对象头中。
优势:减少了一次指针定位对象实例数据的开销,速度更快。

深入理解java虚拟机笔记Chapter2的更多相关文章
- Java内存区域与内存溢出异常——深入理解Java虚拟机 笔记一
Java内存区域 对比与C和C++,Java程序员不需要时时刻刻在意对象的创建和删除过程造成的内存溢出.内存泄露等问题,Java虚拟机很好地帮助我们解决了内存管理的问题,但深入理解Java内存区域,有 ...
- 深入理解java虚拟机笔记Chapter12
(本节笔记的线程收录在线程/并发相关的笔记中,未在此处提及) Java内存模型 Java 内存模型主要由以下三部分构成:1 个主内存.n 个线程.n 个工作内存(与线程一一对应) 主内存与工作内存 J ...
- 深入理解Java虚拟机笔记
1. Java虚拟机所管理的内存 2. 对象创建过程 3. GC收集 4. HotSpot算法的实现 5. 垃圾收集器 6. 对象分配内存与回收细节 7. 类文件结构 8. 虚拟机类加载机制 9.类加 ...
- 深入理解java虚拟机笔记Chapter7
虚拟机类的加载机制 概述 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类的加载机制. 类加载的时机 J ...
- 深入理解java虚拟机笔记之一
Java的技术体系主要有支撑java程序运行的虚拟机,提供各开发领域接口支持Java API,java编程语言及许多第三方java框架( 如Spring,Structs等)构成. 可以把Java程序设 ...
- 深入理解Java虚拟机笔记——虚拟机类加载机制
目录 概述 动态加载和动态连接 类加载的时机 类的生命周期 被动引用 例子一(调用子类继承父类的字段) 例子二(数组) 例子三(静态常量) 类加载的过程 加载 验证 准备 解析 符号引用 直接引用 初 ...
- 【转载】深入理解Java虚拟机笔记---运行时栈帧结构
栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区的虚拟机栈(Virtual Machine Stack)的栈元素.栈帧存储了方法的局部变量表,操作 ...
- 深入理解java虚拟机笔记Chapter8
运行时栈帧结构 栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈(Virtual Machine Stack)的栈元素.栈帧存储了方法 ...
- 类文件结构——深入理解Java虚拟机 笔记三
在之前的笔记中记录过,Java程序变成可执行文件的步骤是:源代码-->经过编译变成class文件-->经过JVM虚拟机变成可执行的二进制文件.因此,为了对JVM执行程序的过程有一个好的了解 ...
随机推荐
- POJ2296二分2sat
题意: 给n个点,每个点必须在一个正方形上,可以在正方向上面边的中点或者是下面边的中点,正方形是和x,y轴平行的,而且所有的点的正方形的边长一样,并且正方形不能相互重叠(边相邻可以),问满 ...
- UVA11388GCD LCM
题意: 输入两个整数G,L,找出两个正整数a,b使得gcd(a ,b)=G,lcm(a ,b)=L,如果有多组解,输出最小的a的那组,如果没解,输出-1. 思路: 比较简单,如 ...
- 【python】Leetcode每日一题-二叉搜索迭代器
[python]Leetcode每日一题-二叉搜索迭代器 [题目描述] 实现一个二叉搜索树迭代器类BSTIterator ,表示一个按中序遍历二叉搜索树(BST)的迭代器: BSTIterator(T ...
- 多变量高斯(MVN)概率建模的两种方案
摘要:在我们的时序异常检测应用中,设计了对时序数据进行多变量高斯(MVN)建模的算法方案进行异常检测,本文对基于tensorflow的两种MVN建模方案进行了总结. 1.基于custom choles ...
- C#中的partial关键字
这节讲一下partial(局部的,部分的)关键字,初学者可能没有接触过这个关键字,但是只要你写过winform或者WPF应用程序的话,那你肯定被动用过这个关键字.首先介绍一下这个关键字的作用,它用作定 ...
- 『居善地』接口测试 — 6、Httpbin服务介绍
目录 1.Httpbin服务介绍 2.在Windows系统中部署Httpbin服务 3.在Linux系统中部署Httpbin服务 4.Httpbin访问方式 5.Httpbin常用调试接口 6.总结: ...
- Convert character array to string in MATLAB
Matlab提取特征值是经常要读取多个图片文件,把文件名保存在数组中后再读取会出错.从stackoverflow中找到如下解决方法: I've a M by N matrix, each cell c ...
- [bug] 安装MySQL8.0.15 失败,提示This application requires Visual Studio 2015 x64 Redistributable
参考 https://blog.csdn.net/weixin_44092289/article/details/88045666
- [刷题] 283 Move Zeros
要求 将所有的0,移动到vector的后面比如; [1,3,0,12,5] -> [1,3,12,5,0] 实现 第一版程序,时间.空间复杂度都是O(n) 1 #include<iostr ...
- [Linux] Linux C编程一站式学习 Part.3
Linux系统编程 文件与I/O C标准I/O库函数与Unbuffered I/O函数 C标准I/O库函数printf().putchar().fputs(),会在用户空间开辟I/O缓冲区 系统函数o ...