当前位置: 首页 > news >正文

openjdk底层(hotspot)汇编指令的内存分布

内存分段机制

在冯诺依曼机中,内存是一整块。数据放在数据段,代码放在代码段,二者之间不能混合。假设内存分布如下。

在这里插入图片描述

JVM的实现

CodeSection类

为了模拟内存中的这种机制,首先定义了CodeSection类。该类用于描述段。

在这里插入图片描述

不考虑重定位计算,该类的关键属性包括下面几个方面

属性名备注
_start数据记录内存起始地址
_end当前数据记录内存终止地址
_limit分配的最后一个地址
_mark这个属性用于标记该段section,一般就是第一条指令
_index用于标记该段属于什么类型。

在这里插入图片描述

注意,通常我们在Java或者C#中描述某种固定类型通常使用enum关键字描述,但由于Java和C#是强类型语言,因此都是用enum的具体值来描述。例如在C#中定义段类型:

public enum EnumCodeSectionType{SECT_CONSTS,   //放置常量值SECT_INST,     //放置指令值SECT_STUB,     //放置装代码SECT_NONE=-1   //非代码段
}public class CodeSection{private EnumCodeSectionType _codeSectionType=;...
}

但是C或者C++是弱类型语言,其枚举类型的本质是整形,因此段枚举如下:

enum {// Here is the list of all possible sections.  The order reflects// the final layout.SECT_FIRST = 0,SECT_CONSTS = SECT_FIRST, // Non-instruction data:  Floats, jump tables, etc.SECT_INSTS,               // Executable instructions.SECT_STUBS,               // Outbound trampolines for supporting call sites.SECT_LIMIT, SECT_NONE = -1};

我们在CodeSection类中,使用_index字段描述该该枚举值。

public class CodeSection{private:int _index;  //默认值由构造函数决定,否则为原内存中的遗留值
}

CodeBuffer类

在JVM中,定义CodeBuffer类用于保存各种CodeSection

在这里插入图片描述

这里可以看到有3个CodeSection类对象,分别是_consts,_insts以及_stubs。这3个字段分别对应3种CodeSection。在内存上的分别可以用下图显示。

在这里插入图片描述

其他相关字段功能如下

字段名用途
_nameCodeBuffer的名称,用于打印信息
_total_start整个CodeBuffer的起始地址
_total_size整个CodeBuffer的大小
_last_insn最后一条指令

如何将指令插入_insts字段

假设使用的电脑架构是x86,使用gdb进行java -version命令的调试
build/linux-x86_64-normal-server-slowdebug/jdk/bin目录下新建.gdbinit文件

handle SIGSEGV pass noprint nostop
handle SIGUSR1 pass noprint nostop
handle SIGUSR2 pass noprint nostop
set logging on
set breakpoint pending onb mainr -Xint -version

这里使用-Xint选项表示采用纯解释器模式执行
使用下面的命令启动gdb调试

gdb -command=.gdbinit ./java

运行后,程序停在main函数处

Breakpoint 1, main (argc=3, argv=0x7fffffffe088)at /home/jx/src/openjdk/src/java.base/share/native/launcher/main.c:98
98      

此时,根据《openjdk底层(hotspot)汇编指令调用(四)——发送指令到内存》中讲述的内容,将断点设置在的下面函数处

//assembler.hpp
void emit_int8(   int8_t  x) { code_section()->emit_int8(   x); }

gdb打印信息如下

(gdb) b emit_int8
Function "emit_int8" not defined.
Breakpoint 2 (emit_int8) pending.
(gdb) c
Continuing.
[New Thread 0x7ffff7fe0700 (LWP 12619)]
[Switching to Thread 0x7ffff7fe0700 (LWP 12619)]Thread 2 "java" hit Breakpoint 2, AbstractAssembler::emit_int8 (this=0x7ffff001bbb0, x=-123 '\205') at /home/jx/src/openjdk/src/hotspot/share/asm/assembler.hpp:273
273       void emit_int8(   int8_t  x) { code_section()->emit_int8(   x); }
(gdb) s
AbstractAssembler::code_section (this=0x7ffff001bbb0)at /home/jx/src/openjdk/src/hotspot/share/asm/assembler.hpp:306
306       CodeSection*  code_section() const   { return _code_section; }
(gdb) print *_code_section
$1 = {_start = 0x7fffec9c20a0 '\314' <repeats 63 times>, <incomplete sequence \314>, _mark = 0x0, _end = 0x7fffec9c20a0 '\314' <repeats 63 times>, <incomplete sequence \314>, _limit = 0x7fffec9c20e0 "", _locs_start = 0x0, _locs_end = 0x0, _locs_limit = 0x0, _locs_point = 0x7fffec9c20a0 '\314' <repeats 63 times>, <incomplete sequence \314>, _locs_own = false, _frozen = false, _scratch_emit = false, _index = 1 '\001', _outer = 0x7ffff7fdf890
}

这里可以看到该CodeSection类对象的信息,

其中_index=1,结合上述的类图以及段类型枚举可知,当前CodeSection是SECT_INST类型,即代码段。

该段的起始地址和终止地址由_start和_end对象描述,由于是第一条插入的指令,因此二者相等

_start = 0x7fffec9c20a0
_end   = 0x7fffec9c20a0

该段最大只能到_limit处

_limit = 0x7fffec9c20e0

对应到内存的图形分布如下
在这里插入图片描述

可以看到该代码段有几个特征。

  • 起始地址能够被8整除,这样有利于CPU访存的速度
  • 整个代码段的长度是64字节,也有利于CPU访存速度

我们再让其运行到emit_8int处看看

Thread 2 "java" hit Breakpoint 2, AbstractAssembler::emit_int8 (this=0x7ffff001bbb0, x=-10 '\366') at /home/jx/src/openjdk/src/hotspot/share/asm/assembler.hpp:273
273       void emit_int8(   int8_t  x) { code_section()->emit_int8(   x); }
(gdb) s
AbstractAssembler::code_section (this=0x7ffff001bbb0)at /home/jx/src/openjdk/src/hotspot/share/asm/assembler.hpp:306
306       CodeSection*  code_section() const   { return _code_section; }
(gdb) print *_code_section
$2 = {_start = 0x7fffec9c20a0 "\205", '\314' <repeats 62 times>, <incomplete sequence \314>, _mark = 0x0, _end = 0x7fffec9c20a1 '\314' <repeats 62 times>, <incomplete sequence \314>, _limit = 0x7fffec9c20e0 "", _locs_start = 0x0, _locs_end = 0x0, _locs_limit = 0x0, _locs_point = 0x7fffec9c20a0 "\205", '\314' <repeats 62 times>, <incomplete sequence \314>, _locs_own = false, _frozen = false, _scratch_emit = false, _index = 1 '\001', _outer = 0x7ffff7fdf890
}

此时可以看到_end的值发生了改变

_start = 0x7fffec9c20a0
_end   = 0x7fffec9c20a1

可以看到_end的地址增加了1个字节,即8位
在这里插入图片描述

我们此时再看看_outer属性,该属性对应了CodeBuffer,即哪个CodeBuffer拥有该CodeSection

(gdb) print *_code_section->_outer
$4 = {<StackObj> = {<AllocatedObj> = {_vptr.AllocatedObj = 0x7ffff6fb7110 <vtable for CodeBuffer+16>}, <No data fields>}, members of CodeBuffer: _name = 0x7ffff6540ab4 "static buffer", _consts = {_start = 0x0, _mark = 0x0, _end = 0x0, _limit = 0x0, _locs_start = 0x0, _locs_end = 0x0, _locs_limit = 0x0, _locs_point = 0x0, _locs_own = false, _frozen = false, _scratch_emit = false, _index = 0 '\000', _outer = 0x7ffff7fdf890}, _insts = {_start = 0x7fffec9c20a0 "\205", '\314' <repeats 62 times>, <incomplete sequence \314>, _mark = 0x0, _end = 0x7fffec9c20a1 '\314' <repeats 62 times>, <incomplete sequence \314>, _limit = 0x7fffec9c20e0 "", _locs_start = 0x0, _locs_end = 0x0, _locs_limit = 0x0, _locs_point = 0x7fffec9c20a0 "\205", '\314' <repeats 62 times>, <incomplete sequence \314>, _locs_own = false, _frozen = false, _scratch_emit = false, _index = 1 '\001', _outer = 0x7ffff7fdf890}, _stubs = {_start = 0x0, _mark = 0x0, _end = 0x0, _limit = 0x0, _locs_start = 0x0, _locs_end = 0x0, _locs_limit = 0x0, _locs_point = 0x0, _locs_own = false, _frozen = false, _scratch_emit = false, _index = 2 '\002', _outer = 0x7ffff7fdf890}, 

看到了吗?这个CodeBuffer只有代码段有效,其他的段(_consts, _stubs)的起始地址都是0x0,而且_insts段的起始地址跟我们上面调试的起始地址一样。

总结

通过assembler生成的汇编指令机器码都放在CodeBuffer_insts字段所指的内存区域中。

http://www.xdnf.cn/news/632899.html

相关文章:

  • 掌阅iReader新形态墨水屏Tango发布:科技与美学共舞,开启灵动阅读新体验
  • C++星际密码破译全国信息素养大赛复赛决赛 C++小学/初中组 算法创意实践挑战赛 内部集训模拟题详细解析
  • SAR ADC的功耗设计
  • 图解BERT
  • 免费开源 PDF 阅读器 自带虚拟打印机功能 多格式兼容
  • 【计算机网络】基于UDP进行socket编程——实现服务端与客户端业务
  • threejs模型对象、材质
  • 健身网l;l;j
  • Java21 并发处理说明
  • Python数据可视化实战:让数据从「数字堆」变成「故事书」
  • git push出现 “HTTP 400 curl 22 The requested URL returned error: 400...“错误
  • 计网5:HTTP/TCP的长连接和短连接的区别以及各自的应用场景
  • JavaSE核心知识点03高级特性
  • Linux系统之----磁盘硬件
  • 【25软考网工】第八章 (1)交换机基础
  • C# 预处理器指令
  • 人工智能概论(一)初见人工智能笔记
  • MultipartFile实现文件上传
  • python 程序实现了毫米波大规模MIMO系统中的信道估计对比实验
  • 基于CEEMDAN-Transformer-BiLSTM的多特征风速气候预测的完整实现方案及PyTorch源码解析
  • 2025年渗透测试面试题总结-匿名[实习]安全工程师(大厂) (2)(题目+回答)
  • 大数据与数据分析【数据分析全栈攻略:爬虫+处理+可视化+报告】
  • Github上一些使用技巧(缩写、Issue的Highlight)自用
  • Ollama-OCR:基于Ollama多模态大模型的端到端文档解析和处理
  • 对象存储(Minio)使用
  • 一周学会Pandas2 Python数据处理与分析-Pandas2数据合并与对比-pd.merge():数据库风格合并
  • 基于蒙特卡罗 AWGN信道调制信号互信息(信道容量)
  • vc++在Windows中查找扬声器输出的当前音量级别
  • DeepSeek+白果AI论文:开启答辩PPT生成的「智能双引擎」时代
  • 国际前沿知识系列二:基于不同类型头部碰撞中的运动学特征预测能力统计分析