画图理解JVM相关内容
文章目录
- 1. JVM视角下,内存划分
- 2. 类内存分布硬核详解
- 1. 获取堆内存参数
- 2. 扫描堆内存,定位实例
- 3. 查看实例所在地址的数据
- 4. 找到实例所指向的类信息的地址
- 5. 查看class信息
- 6. 结论
- 3. Java的对象创建流程
- 4. 垃圾判别算法
- 4.1 引用计数法
- 4.2 可达性分析算法
- 5. 垃圾收集算法
- 5.1 标记-清除算法
- 5.2 标记-复制算法
- 5.3 标记-整理算法
1. JVM视角下,内存划分
tip: 额外补充
- 在以“分代设计”为主导的堆内存,其控件划分大致如上图所示。但G1垃圾回收期为分解,后续的内存设计并没有都参考分代理论,因此jdk8以后(G1大规模运用在jdk8之后),内存划分有待商榷
- 堆虽然是线程共享的,但他可以为线程划分缓冲区——Thread Local Allocation Buffer,TLAB。TLAB是线程私有的。但无论怎么划分,堆都是存储对象实例
- 直接内存:属于操作系统本地内存,不归JVM管理。因此GC对他无效
2. 类内存分布硬核详解
既然是硬核,不来点内存轰炸是对不起硬核两字。
下文主要讲述一个类在创建过程中,可能会涉及到的所有类在内存的分布情况。包括JVM层面的instanceKlass,Java层面的Test实例,Test.class
下文内容比较硬核,请读者酌情阅读。另外,底层指针分析可能存在纰漏,欢迎读者友善指出
让我们开始!
demo代码如下
package com.xhf.test;// -XX:+UseSerialGC -Xmn10M -XX:-UseCompressedOops
public class TestDemo {public static void main(String[] args) {new Test();while (true) {}}
}
package com.xhf.test;public class Test {private static Integer a;private Integer b;private int c;public int d;private void func() {}public void func2() {}
}
1. 获取堆内存参数
打开HSDB,扫描堆的整体内存范围 universe
Heap Parameters:
Gen 0: eden [0x0000000080000000,0x00000000803845a8,0x0000000080800000) space capacity = 8388608, 43.96257400512695 usedfrom [0x0000000080800000,0x0000000080800000,0x0000000080900000) space capacity = 1048576, 0.0 usedto [0x0000000080900000,0x0000000080900000,0x0000000080a00000) space capacity = 1048576, 0.0 usedInvocations: 0Gen 1: old [0x0000000080a00000,0x0000000080a00000,0x000000008fe00000) space capacity = 255852544, 0.0 usedInvocations: 0
其它信息我们可以不用关注,只需要知道,eden区的范围是0x0000000080000000 0x0000000080800000
,绝大多数情况下,对象的空间有限划分在eden区域。因此,我们想要探查Test示例相关内存地址,需要扫描eden区域
2. 扫描堆内存,定位实例
scanoops 0x0000000080000000 0x0000000080800000 com.xhf.test.Test
hsdb> scanoops 0x0000000080000000 0x0000000080800000 com.xhf.test.Test
0x000000008023e2d0 com/xhf/test/Test
主程序运行new Test();
,他的实例对象被划分在0x000000008023e2d0
地址
3. 查看实例所在地址的数据
inspect 0x000000008023e2d0
hsdb> inspect 0x000000008023e2d0
instance of Oop for com/xhf/test/Test @ 0x000000008023e2d0 @ 0x000000008023e2d0 (size = 32)
_mark: 1
_metadata._klass: InstanceKlass for com/xhf/test/Test
b: null null
c: 0
d: 0
在控制台上通过指令,查看不到最全面的信息,通过Tools->inspector创建可视化窗口,可以查看最全面的信息,具体如下
通过上述两幅图,我们可以返现很多有趣的细节
- _mark字段,mark其实就是markword,对象头的意思。markword能够存储相当丰富的信息,比如分代年龄,gc次数,偏向锁,重锁等等信息。
- _metadata._klass,类型指针,指向类型com.xhf.test.Test.class。该字段用于表示当前实例是哪个类的实例
- b, c, d:3个字段属于oop,但a不属于oop,a属于Test.class,因为他是静态变量。此外,b这个Object被赋值null,c,d两个基本int类型赋值为0
4. 找到实例所指向的类信息的地址
我们找到Test oop,但没有找到存储Test类信息的数据地址。inspect无法直接看到_metadata._klass指向的地址,我们通过内存扫描,直接查看内存数据
mem 0x000000008023e2d0 2
:查看0x000000008023e2d0地址,偏移2个单位(8bit)
hsdb> mem 0x000000008023e2d0 2
0x000000008023e2d0: 0x0000000000000001
0x000000008023e2d8: 0x0000000013ff3400
0x0000000013ff3400,就是oop指向的Test类信息所在地址
注意,笔者这里并没有说明0x0000000013ff3400是Test.class类对象的地址
5. 查看class信息
如下图所示,0x0000000013ff3400才是class真正的信息,这也被称为元信息,被JVM存储在meta space中
!!!需要注意的是,0x0000000013ff3400地址上的内容不是Java意义上的Test.class这个类
笔者为什么会这么说呢?原因是JVM内部采用C++的instanceKlass描述
Java类,并且会将instanceKlass分配到meta space
而instanceKlass有个叫做_java_mirror的字段,它指向的才是Java类的Class对象
本例中就是Test.class这个对象
我们监视这个地址inspect 0x000000008023e210
hsdb> inspect 0x000000008023e210
instance of Oop for java/lang/Class @ 0x000000008023e210 @ 0x000000008023e210 (size = 168)
a: null null
发现_java_mirror
指向的对象,是java/lang/Class
类(Test.class),并且大小168bit
我们扫描0x000000008023e210往后的168bit(21个8bit)内存空间
mem 0x000000008023e210 21
hsdb> mem 0x000000008023e210 21
0x000000008023e210: 0x0000000000000001
0x000000008023e218: 0x0000000013c03ed0
0x000000008023e220: 0x0000000000000000
0x000000008023e228: 0x0000000000000000
0x000000008023e230: 0x0000000000000000
0x000000008023e238: 0x00000000800dba38
0x000000008023e240: 0x0000000000000000
0x000000008023e248: 0x0000000000000000
0x000000008023e250: 0x0000000000000000
0x000000008023e258: 0x0000000000000000
0x000000008023e260: 0x0000000000000000
0x000000008023e268: 0x0000000000000000
0x000000008023e270: 0x0000000000000000
0x000000008023e278: 0x0000000080239560
0x000000008023e280: 0x0000000000000000
0x000000008023e288: 0x0000000000000000
0x000000008023e290: 0x0000000013ff3400
0x000000008023e298: 0x0000000000000000
0x000000008023e2a0: 0x0000001500000000
0x000000008023e2a8: 0x0000000000000001
0x000000008023e2b0: 0x0000000000000000
发现内存地址为0x000000008023e290
时,存放的数据是:0x0000000013ff3400
而0x0000000013ff3400
的内容,恰好是instanceKlass所在地址。
6. 结论
基于上述分析,我们得出如下结论:
Test实例 -> Test instanceKlass <-> Test.class
文字枯燥乏味,看图就好理解了
3. Java的对象创建流程
有了第2节的基础,第三节的分析自然就简单多了。
具体流程直接上图
这个流程中,具体的内存情况如下
tip:
严格来说,上图存在一定的问题。
由第2节可知,实例的指针指向的是instanceKlass,而非class对象。这里这么处理是为了方便画图。
而且,instanceKlass拥有class对象的指针,实例可以通过instanceKlass找到class对象,只是需要两次指针跳跃,所以上图绘制方式其实也并无太大问题
4. 垃圾判别算法
4.1 引用计数法
给对象增加计数器,当计数器为0,表示对象不再被引用。可以当作垃圾被垃圾清除器清理
这种算法的缺陷很明显,一方面开销大,JVM需要维护所有对象的引用计数器;另一方面,无法解决循环引用的问题
4.2 可达性分析算法
以GC Root根节点的集合,作为起始点。按照对象之间的引用关系向下遍历,如果某个对象无法和GC Root关联,那么我们认为该对象是不可达的,可以当作垃圾被回收
5. 垃圾收集算法
在讲解回收算法前,我们需要补充一些分代理论的基础知识
- 大部分对象都是朝生幕死,创建出来很快就被回收
- 如果一个对象经历了多次垃圾回收,那么该对象可以被认为是长时间存活的对象
曾经有个组织做过调查,98%的对象活不过一轮垃圾回收
考虑到对象存活时间长短存在差异,我们可以大致将堆内存划分为两块空间
- 新生代(Young Generation)
- 老年代(Old Generation)
新生代存放寿命短的对象;老年代存放长命的对象。这样在做垃圾回收时,可以根据不同区域对象存活特点做出不一样的垃圾回收策略,以此提高运行效率
5.1 标记-清除算法
标记清楚算法是最基础的垃圾回收算法,后续的算法基本都是在此基础上进行改进。
该算法的核心是
- 标记垃圾(可达性分析算法)
- 清除垃圾
标记-清除算法执行流程如上图所示
上述算法存在以下两个缺陷
- 算法效率不稳定:如果内存中存在大量需要清除的垃圾,JVM需要执行多次的清除操作;反之,如果垃圾数量较少,JVM执行清除操作次数就少
- 空间碎片:当JVM执行清除操作后,会存在大量内存碎片,内存中使用的空间不连续。这极大的降低了内存利用率,提高了内存申请的难度
5.2 标记-复制算法
标记-复制算法,将内存划分为等大的两个空间,一个空间用于存放对象,另一个空间用于预留。
当需要进行内存清除时,操作异常容易,因为两个区间在同一时刻只有一个区间存在使用的对象,因此只需要将存放对象的空间中,存活的对象复制到预留空间,然后清除原有空间的所有内容,即可完成垃圾回收
该算法让JVM只需要关注存活的对象,如果存活对象少,那么复制操作少,效率高,因此标记-复制算法一般用于Eden区域的垃圾回收。此外,该算法成功解决了内存碎片的问题
但显而易见,该算法带来了另一个问题
- 内存利用率低:该算法需要额外的空间进行存储,比标记清除算法大了1倍的空间
5.3 标记-整理算法
该算法就是在标记-清除的基础上,增加了整理的操作。对于清除后的内存空间,该算法会通过移动已使用的空间,让内存的使用再次连续
该算法解决了内存碎片问题,但移动存活对象这个操作引入了新的问题。就比如原先对象A引用了对象B,现在B的地址修改了,A如何感知到。此外,在移动过程中,需要暂停用户线程(Stop the world),因此需要移动的对象数量要尽可能少,以此减少stop the world的时间
相关文章:

画图理解JVM相关内容
文章目录 1. JVM视角下,内存划分2. 类内存分布硬核详解1. 获取堆内存参数2. 扫描堆内存,定位实例3. 查看实例所在地址的数据4. 找到实例所指向的类信息的地址5. 查看class信息6. 结论 3. Java的对象创建流程4. 垃圾判别算法4.1 引用计数法4.2 可达性分析…...

Scikit-Learn K均值聚类
Scikit-Learn K均值聚类 1、K均值聚类1.1、K均值聚类及原理1.2、K均值聚类的优缺点1.3、聚类与分类的区别2、Scikit-Learn K均值聚类2.1、Scikit-Learn K均值聚类API2.2、K均值聚类初体验(寻找最佳K)2.3、K均值聚类案例1、K均值聚类 K-均值(K-Means)是一种聚类算法,属于无…...

蓝桥杯 - 受伤的皇后
解题思路: 递归 回溯(n皇后问题的变种) 在 N 皇后问题的解决方案中,我们是从棋盘的顶部向底部逐行放置皇后的,这意味着在任何给定时间,所有未来的行(即当前行之下的所有行)都还没…...

AcWing---乌龟棋---线性dp
312. 乌龟棋 - AcWing题库 思路: 原来没有碰到过类似的题: dp数组为思维:dp[i][j][k][r],分别表示用了i个第一类型卡片,j个第二类型卡片...所到的格子数的最大分数,为啥不用记录乌龟到了哪里呢࿱…...

python代码使用过程中使用快捷键注释时报错
1.代码 2.代码报错 3.代码注释后的结果 4. 原因...

go之web框架gin
介绍 Gin 是一个用 Go (Golang) 编写的 Web 框架。 它具有类似 martini 的 API,性能要好得多,多亏了 httprouter,速度提高了 40 倍。 如果您需要性能和良好的生产力,您一定会喜欢 Gin。 安装 go get -u github.com/gin-gonic/g…...

SpringBoot 定时任务实践、定时任务按指定时间执行
Q1. springboot怎样创建定时任务? 很显然,人人都知道,Scheduled(cron ".....") Q2. 如上所示创建了定时任务却未能执行是为什么? 如果你的cron确定没写错的话 cron表达式是否合法,可参考此处,…...

MYSQL数据库故障排除与优化
目录 MySQL 单实例故障排查 MySQL 主从故障排查 MySQL 优化 MySQL 单实例故障排查 故障现象 1 ERROR 2002 (HY000): Cant connect to local MySQL server through socket /data/mysql/mysql.sock (2) 问题分析:以上这种情况一般都…...

算法-数论-蓝桥杯
算法-数论 1、最大公约数 def gcd(a,b):if b 0:return areturn gcd(b, a%b) # a和b的最大公约数等于b与a mod b 的最大公约数def gcd(a,b):while b ! 0:cur aa bb cur%bpassreturn a欧几里得算法 a可以表示成a kb r(a,b,k,…...

222.完全二叉树节点个数
给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。 完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最…...

C++中的string类操作详解
引言 针对C中的string,本文主要讲解如何对其进行插入、删除、查找、比较、截断、分割以及与数字之间的相互转换等。 字符串插入 1. append方法 std::string str "hello"; str.append(7, w); // 在末尾添加7个字符w str.append("wwwwwww");…...

Java绘图坐标体系
一、介绍 下图说明了Java坐标系。坐标原点位于左上角,以像素为单位。在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐…...
【MATLAB源码-第38期】基于OFDM的块状导频和梳状导频误码率性能对比,以及LS/LMMSE两种信道估计方法以及不同调制方式对比。
操作环境: MATLAB 2022a 1、算法描述 块状导频和梳状导频都是用于无线通信系统中信道估计的方法。 块状导频: 定义: 在频域上,块状导频是连续放置的一组导频符号。这意味着所有的导频符号都集中在一个短的时间段内发送。 优点…...

javaWeb车辆管理系统设计与实现
摘 要 随着经济的日益增长,车辆作为最重要的交通工具,在企事业单位中得以普及,单位的车辆数目已经远远不止简单的几辆,与此同时就产生了车辆资源的合理分配使用问题。 企业车辆管理系统运用现代化的计算机管理手段,不但可以对车辆的使用进行合理的管理,…...

【DM8】间隔分区
是范围分区的一个扩展 如果使用了间隔函数做分区,在数据插入的时候,如果没有合适的分区,数据库会自动创建一个新的分区。 –year往后推两年 SELECT SYSDATE numtoyminterval(2,‘YEAR’); –month往后推两年 SELECT SYSDATE numtoyminterv…...

0基础如何进入IT行业?
目录 0基础如何进入IT行业? 一、学习路径 二、技能培养 三、实践经验 0基础如何进入IT行业? 对于没有任何相关背景知识的人来说,成功进入IT行业可能看起来是一个遥不可及的目标。然而,只要有正确的方法和坚持不懈的努力&#…...

C#将Console写至文件,且文件固定最大长度
参考文章 将C#的Console.Write同步到控制台和log文件输出 业务需求 在生产环境中,控制台窗口不便展示出来。 为了在生产环境中,完整记录控制台应用的输出,选择将其输出到文件中。 但是,一次性存储所有输出的话,文件会…...

《CSS 知识点》仅在文本有省略号时添加 tip 信息
html <div ref"btns" class"btns"><div class"btn" >这是一段很短的文本.</div><div class"btn" >这是一段很短的文本.</div><div class"btn" >这是一段很长的文本.有省略号和tip.<…...

彩虹聚合DNS管理系统v1.0全新发布
聚合DNS管理系统(https://github.com/netcccyun/dnsmgr)可以实现在一个网站内管理多个平台的域名解析,目前已支持的域名平台有:阿里云、腾讯云、华为云、西部数码、CloudFlare。本系统支持多用户,每个用户可分配不同的…...

3.10 Python数据类型转换
Python类型转换,Python数据类型转换函数大全 虽然 Python 是弱类型编程语言,不需要像Java或 C 语言那样还要在使用变量前声明变量的类型,但在一些特定场景中,仍然需要用到类型转换。 比如说,我们想通过使用 print() …...

Kotlin基础学习
Kotlin基础学习主要涵盖安装Kotlin编译器、了解基础语法、学习变量声明、类型推断、函数定义以及控制结构等方面。以下是一个简要的Kotlin基础学习指南: 一、安装Kotlin 首先,你需要从JetBrains的官方网站下载并安装Kotlin编译器。同时,你也…...

配置交换机 SSH 管理和端口安全——实验1:配置交换机基本安全和 SSH管理
实验目的 通过本实验可以掌握: 交换机基本安全配置。SSH 的工作原理和 SSH服务端和客户端的配置。 实验拓扑 交换机基本安全和 SSH管理实验拓扑如图所示。 交换机基本安全和 SSH管理实验拓扑 实验步骤 (1)配置交换机S1 Switch>enab…...

海山数据库(He3DB)原理剖析:浅析Doris跨源分析能力
Doris湖仓分析背景: Doris多数据源功能演进 Doris的生态近年来围绕湖仓分析做了较多工作,Doris一直在积极拓宽大数据生态的OLAP分析市场,Doris2.0之后为了满足湖仓分析场景,围绕multi-catalog、数据缓存、容错、pipeline资源管理…...

第十三届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组 题解
VP比赛链接 : 数据加载中... - 蓝桥云课 1 . 九进制 转 十进制 直接模拟就好了 #include <iostream> using namespace std; int main() {// 请在此输入您的代码int x 22*92*81*9;cout << x << endl ;return 0; } 2 . 顺子日期 枚举出每个情况即可 : …...

20240324-1-集成学习面试题EnsembleLearning
集成学习面试题 1. 什么是集成学习算法? 集成学习算法是一种优化手段或者策略,将多个较弱的模型集成模型组,一般的弱分类器可以是决策树,SVM,KNN等构成。其中的模型可以单独进行训练,并且它们的预测能以某…...

默克尔(Merkle)树 - 原理及用途
默克尔(Merkle)树的原理以及用途 引言 在当今数字化时代,确保数据的完整性是至关重要的。默克尔树作为一种高效的数据结构,被广泛应用于网络安全、分布式系统以及加密货币等领域,用于验证大量数据的完整性和一致性 数…...

设计模式:迭代器模式
迭代器模式的示例可以涵盖各种数据结构的遍历,包括数组、列表、树、图等。下面是一些不同场景下迭代器模式的示例及其代码实现。 示例 1: 数组遍历 使用迭代器模式遍历数组。 // 迭代器接口 interface Iterator<T> {boolean hasNext();T next(); }// 数组迭…...

Navicat Premium 16常用快捷键
打开一个新的查询窗口: Ctrl Q 关闭当前窗口: Ctrl W 运行当前窗口的SQL语句: Ctrl R 运行选中的SQL语句: Ctrl Shift R 注释选中的SQL语句: Ctrl / 取消注释SQL: Ctrl Shift / 保存连接&…...

LeetCode笔记——1042.不邻接植花
题目 有 n 个花园,按从 1 到 n 标记。另有数组 paths ,其中 paths[i] [xi, yi] 描述了花园 xi 到花园 yi 的双向路径。在每个花园中,你打算种下四种花之一。 另外,所有花园 最多 有 3 条路径可以进入或离开. 你需要为每个花园…...

Centos7搭建 Skywalking 单机版
介绍 Skywalking是应用性能监控平台,可用于分布式系统,支持微服务、云原生、Docker、Kubernetes 等多种架构场景。 整体架构如图 Agent :在应用中,收集 Trace、Log、Metrics 等监控数据,使用 RPC、RESTful API、Kafk…...