画图理解JVM相关内容

news2025/1/18 13:49:46

文章目录

    • 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 used
  from [0x0000000080800000,0x0000000080800000,0x0000000080900000) space capacity = 1048576, 0.0 used
  to   [0x0000000080900000,0x0000000080900000,0x0000000080a00000) space capacity = 1048576, 0.0 usedInvocations: 0

Gen 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创建可视化窗口,可以查看最全面的信息,具体如下
在这里插入图片描述

在这里插入图片描述
通过上述两幅图,我们可以返现很多有趣的细节

  1. _mark字段,mark其实就是markword,对象头的意思。markword能够存储相当丰富的信息,比如分代年龄,gc次数,偏向锁,重锁等等信息。
  2. _metadata._klass,类型指针,指向类型com.xhf.test.Test.class。该字段用于表示当前实例是哪个类的实例
  3. 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的时间

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1572191.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DRF:认证(单视图或全局设置认证方案和源码分析、设置多个认证方案、如何设置不允许匿名访问)

概念&#xff1a;request.user、request.auth、认证方案authentication_classes 官网原文&#xff1a; 验证始终在视图的最开始进行&#xff0c;在执行权限和限制检查之前以及允许任何其他代码继续执行之前。 request.user 属性通常被设置为contrib.auth 包中 User 类的一个…

数学知识--(欧拉函数,快速幂,扩展欧几里得算法)

本文用于记录个人算法竞赛学习&#xff0c;仅供参考 目录 一.欧拉函数 二.欧拉函数模板 三.用筛法求每个数的欧拉函数 四.快速幂 五.扩展欧几里得算法 六.用扩展欧几里得算法求线性同余方程 一.欧拉函数 即有一个数n&#xff0c; n通过质因数分解得到 通过欧拉函数有 证明&…

Java 程式 main 方法传参数

Java 程式运行时如果需要传递参数时&#xff0c;常用的方法有两种&#xff1a; 使用 Program Arguments 来传递值使用 VM Arguments 来传递值 1、使用 Program Arguments 来传递值 使用 Program Arguments 来传递值时&#xff0c;main 方法的写法如下&#xff1a; public st…

Linux 常用命令(持续更新中...)

1. ls 查看文件列表命令 语法&#xff1a; ls [-a -l -h] [Linux路径] -a -l -h 是可选的选项 &#xff08;-h需配合-l命令一起使用&#xff09;Linux路径是此命令可选的参数 ls #查看当前目录所有非隐藏文件(平铺方式显示) ls -a #查看当前目录下所有文件 …

Web 后台项目,权限如何定义、设置、使用:菜单权限、按钮权限 ts element-ui-Plus

Web 后台项目&#xff0c;权限如何定义、设置、使用&#xff1a;菜单权限、按钮权限 ts element-ui-Plus 做一个后台管理项目&#xff0c;里面需要用到权限管理。这里说一下权限定义的大概&#xff0c;代码不多&#xff0c;主要讲原理和如何实现它。 一、权限管理的原理 权限…

Polardb MySQL 产品架构及特性

一、产品概述; 1、产品族 参考&#xff1a;https://edu.aliyun.com/course/3121700/lesson/341900000?spma2cwt.28120015.3121700.6.166d71c1wwp2px 2、polardb mysql架构优势 1&#xff09;大容量高弹性&#xff1a;最大支持存储100T&#xff0c;最高超1000核CPU&#xff0…

55、美国德克萨斯大学奥斯汀分校、钱德拉家族电气与计算机工程系:通过迁移学习解决BCI个体差异性[不得不说,看技术还得是老美]

2024年2月5日跨被试最新文章&#xff1a; 德州州立大学奥斯汀分校研究团队最近的一项研究成果&#xff0c;通过非侵入式的脑机接口&#xff0c;可以让被试不需要任何校准就可以使用脑机接口设备&#xff0c;这意味着脑机接口具备了大规模被使用的潜力。 一般来说&#xff0c;…

哈希-字母异位词分组

字母异位词&#xff0c;词频一样&#xff0c;但是顺序不一样&#xff0c;可以进行排序&#xff0c;获取一个key&#xff0c;放在map中即可。 class Solution {public List<List<String>> groupAnagrams(String[] strs) {Map<String, List<String>> ma…

彩虹聚合DNS管理系统v1.0全新发布

聚合DNS管理系统&#xff08;https://github.com/netcccyun/dnsmgr&#xff09;可以实现在一个网站内管理多个平台的域名解析&#xff0c;目前已支持的域名平台有&#xff1a;阿里云、腾讯云、华为云、西部数码、CloudFlare。本系统支持多用户&#xff0c;每个用户可分配不同的…

STM32CubeIDE基础学习-舵机控制实验

STM32CubeIDE基础学习-舵机控制实验 文章目录 STM32CubeIDE基础学习-舵机控制实验前言第1章 硬件介绍第2章 工程配置2.1 基础工程配置部分2.2 生成工程代码部分 第3章 代码编写第4章 实验现象总结 前言 SG90、MG996舵机在机器人领域用得非常多&#xff0c;因为舵机有内置控制电…

利用nginx-http-flv-module实现三种直播

目录 一、说明 二、目标 三、实现 四、直播地址 一、说明 此文在《流媒体服务器的搭建(支持hls)》《搭建nginx-http-flv-module直播系统》之后编写,很多详细内容需要参考它。 流媒体服务器的搭建(支持hls)

【面经】interrupt()、interrupted()和isInterrupted()的区别与使用

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;面经 ⛺️稳中求进&#xff0c;晒太阳 interrupt方法 如果打断线程正在sleep&#xff0c;wait&#xff0c;join会导致被打断的线程抛出InterruptedException&#xff0c;并清除打断标记。如…

商标“五分法”,如何起名显著性更强通过率更高!

1976年在Abercrombie一案美国判例中提出的商标五分法&#xff0c; 基本上在全球范围内得到认可和共识&#xff0c;普推知产老杨平常检索时&#xff0c;我国一些专家相关的论文及专著和判例中也会经常涉及到。 商标五分法主要是把商标分成个五种类型&#xff0c; 通用的&#xf…

Linux:数据链路层

文章目录 路由表数据链路层分片mac帧报头ARP协议ARP的周边话题 路由表 当主机a想要发送消息到主机b&#xff0c;这一整个过程中&#xff0c;数据报文在进行传输的过程实际上是一跳一跳的过去的&#xff0c;而报文可能会经过公网进行传递&#xff0c;本质上这些网络都是靠对应的…

【Arthas案例】某应用依赖两个GAV-classifier不同的snakeyaml.jar,引起NoSuchMethodError

多个不同的GAV-classifier依赖冲突&#xff0c;引起NoSuchMethodError Maven依赖的三坐标体系GAV(G-groupId&#xff0c;A-artifactId&#xff0c;V-version) classifier通常用于区分从同一POM构建的具有不同内容的构件物&#xff08;artifact&#xff09;。它是可选的&#xf…

美食分享|基于Springboot和vue的地方美食分享网站系统设计与实现(源码+数据库+文档)

地方美食分享网站系统 目录 基于Springboot和vue的地方美食分享网站系统设计与实现 一、前言 二、系统设计 三、系统功能设计 1、前台&#xff1a; 2、后台 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介…

四信AI智能视频边缘分析盒+传感云平台,开启食品安全智慧监管新模式

方案背景 民以食为天&#xff0c;食品是人类生存必备的物质之一&#xff0c;食品生产安全关乎每个人的生命健康与社会可持续发展。在食品生产过程中&#xff0c;如何实现安全、健康生产是监管机构首要考虑因素&#xff0c;也是当今社会必须共同关注与努力的方向。 监管机构必…

数据库性能优化入门:数据库分片初探

数据库分片是一种用于提升数据库性能的架构模式&#xff0c;选择正确的分片策略和实施方式对于提高数据库性能和应对大规模数据挑战至关重要。 本文介绍了数据库分片的定义、原理和实施方法。文章解释了数据库分片是如何通过将数据切分、分散存储在多个服务器上来提升性能&…

【Kotlin】委托模式

1 委托模式简介 委托模式的类图结构如下。 对应的 Kotlin 代码如下。 fun main() {var baseImpl BaseImpl()var baseWrapper BaseWrapper(baseImpl)baseWrapper.myFun1() // 打印: BaseImpl, myFun1baseWrapper.myFun2() // 打印: BaseImpl, myFun2 }interface Base {fun my…

C语言第四十弹---预处理(下)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 预处理 1、#和## 1.1 #运算符 1.2、##运算符 2、命名约定 3、#undef 4、命令行定义 5、条件编译 6、头文件的包含 6.1、头文件被包含的方式 6.1.1、本地…