C++指针和地址偏移在HotSpot VM中的应用
在前面我们介绍过new运算符,这个操作实际上上包含了如下3个步骤:
- 调用operator new的标准库函数。此函数会分配一块内存空间以便函存储相应类型的实例。
- 调用相应类的构造函数
- 返回一个指向该对象的指针
在第一步中,其实我们可以自己写个operator new函数对标准库函数进行重载,通常会根据类信息分配出需要的内存大小,但是分配内存的逻辑现在由我们自己控制,那我们就可以多分配一些内存,然后在多分配出来的内存上存储一些额外定义的信息。例如:
class Test {
private:
int a = 1;
public:
void *operator new(size_t requested_size) throw() {
return ::malloc(requested_size);
}
void *operator new(size_t requested_size, size_t length) throw() {
return ::malloc(requested_size + length);
}
long access_a_offset() {
return (size_t) ((intptr_t) &(((Test *) 16)->a) - 16);
}
u_char *start_b_address() {
return (u_char *) this + 4;
}
void set_b_value(long val) {
*start_b_address() = val;
}
long get_b_value() {
return (long) (*start_b_address());
}
};
我们重载了new运算符,第一个重载函数会分配类实例本来需要的内存大小,而第二个重载函数多分配了length个字节的大小。举个具体使用的例子,如下:
std::cout << sizeof(Test) << std::endl;
// 调用第一个operator new函数,分配的内存大小为8,用来存储变量
Test *t1 = new Test();
// 调用第二个operator new函数,分配的内存大小为16,用来存储变量外,还有空闲的8字节
Test *t2 = new (8) Test(); std::cout << t2->access_a_offset() << std::endl;
t2->set_b_value(10);
std::cout << t2->get_b_value() << std::endl;
最终打印的值为0 10
如上的例子在内存末尾多开辟了8字节用来存储long类型的数据,因为这个数据没有对应的属性用来直接存取,所以只能通过偏移来操作。
注意:我们通过this获取到当前实例内存的首地址时,必须要强制转换为u_char*类型,这样加4后才会移动4个字节,因为u_char占用一个字节,此时的指针指向u_char数据类型。其实还可以这样获取:
(u_char *) (this + 1)
this指向的是Test类型,加1后指针本身占用的内存大小的末尾,将其强制转换为指向u_char类型的指针即可。
在HotSpot VM中也有这样的操作,例如Method,根据需要有两个可选择性的字段,如下:

根据Method决定是否要多开辟内存来存储native_function和signature_handler,源代码如下:
int size = Method::size(access_flags.is_native()); return new (loader_data, size, false, MetaspaceObj::MethodType, THREAD) Method(cm, access_flags, size);
当为本地方法时,会为Method多开辟2 个指针大小的存储空间,然后使用new关键字创建对象。这里也重载了new运算符从指定的元数据区分配内存。
下面来看对这两个伪字段(不能通过类中的实例字段进行存取操作,但是又确实存在)的存取操作。
typedef u_char* address;
address* native_function_addr() const {
assert(is_native(), "must be native");
return (address*) (this+1);
}
address* signature_handler_addr() const {
return native_function_addr() + 1;
}
获取两个伪字段的地址。
注意这里的this+1,因为this的类型是Method实例,所以加1并不是加一个字节而是增加一个Method对应的字节数,即获取Method对应内存区域的下一个字节的地址;第二个native_function_addr() + 1,因为native_function_addr()返回的就是一个指针类型的数据,所以这里的加1是增加指针对应的字节数,64位下是8字节。
返回的类型为u_char**,也就是返回一个指向指针的指针。当我们要存储本地函数地址时,可如下操作:
// 读取操作
address current = *native_function; // 存储操作
*native_function = function
其中的function的类型为address。
下面继续看偏移量的操作,HotSpot VM中经常做的操作就是计算某个变量的偏移量。例如定义的用来表示Java类的C++类Klass中有如下2个函数:
static ByteSize access_flags_offset(){
return in_ByteSize(offset_of(Klass, _access_flags));
}
其中的_access_flags属性就是定义在Klass中的,通过调用access_flags_offset()函数来计算这个属性在类中的偏移量。offset_of是一个宏,如下:
#define offset_of(klass,field) (size_t)((intx)&(((klass*)16)->field) - 16)
经过宏替换和格式调整后的方法如下:
static ByteSize access_flags_offset(){
return in_ByteSize((size_t)(
(intx)&( ((Klass*)16)->_access_flags) - 16
));
}
通过 (intx)&(((Klass*)16)->_access_flags) - 16 方式来计算出具体的偏移量。解释一下这种写法。
假如定义个变量Klass a; 我们都知道&a表示变量a的首地址,&(a._access_flags)表示变量_access_flags的地址,那么&(a._access_flags)减去&a就得到_access_flags的偏移量。
((Klass*)16)的地址为16,所以偏移量最终等于&( ((Klass*)16)->_access_flags)减去16。
当HotSpot VM要用一个成员变量的时候,它会根据对象的首地址加上成员的偏移量得到成员变量的地址。当对象的首地址为0时,得到的成员变量地址就是它的偏移量。
本人最近准备出一个手写Hotspot VM的课程,超级硬核,从0开始写HotSpot VM,将HotSpot VM所有核心的实现全部走一遍,如感兴趣,速速入群。
群里可讨论虚拟机和Java性能剖析与故障诊断等话题,欢迎加入。
C++指针和地址偏移在HotSpot VM中的应用的更多相关文章
- JVM详解之:HotSpot VM中的Intrinsic methods
目录 简介 什么是Intrinsic Methods 内置方法的特点 多样性 兼容性 java语义的扩展 Hotspot VM中的内置方法 intrinsic方法和内联方法 intrinsic方法的实 ...
- HotSpot VM 中的JIT分类
在HotSpot VM中内嵌有两个JIT编译器,分别为Client Compiler和Server Compiler,但大多数情况下我们简称为C1编译器和C2编译器.开发人员可以通过如下命令显式指定J ...
- 014-通过JDB调试,通过HSDB来查看HotSpot VM的运行时数据
一.JDB调试 在预发环境下进行debug时,时常因为工具和环境的限制,导致debug体验非常差,那么有什么方法能够简化我们进行debug的体验吗?JDB就是一种. JDB ...
- HotSpot VM运行时
HotSpot VM运行时系统为HotSpot JIT编译器和垃圾收集器提供服务和通用API,同时还为VM提供启动.线程管理.JNI(Java本地接口)等基本功能.HotSpot VM运行时环境担当许 ...
- 转:什么是即时编译(JIT)!?OpenJDK HotSpot VM剖析
重点 应用程序可以选择一个适当的即时编译器来进行接近机器级的性能优化. 分层编译由五层编译构成. 分层编译提供了极好的启动性能,并指导编译的下一层编译器提供高性能优化. 提供即时编译相关诊断信息的JV ...
- c++入门之出话指针和地址。
指针和地址是c和c++中重要的概念,在此,对指针做以下几方面的总结: new和delete: ]; point[] = ; point[] = ; point[] = ; cout << ...
- [转]HotSpot VM GC 的种类
原文地址:http://www.cnblogs.com/redcreen/archive/2011/05/04/2037029.html collector种类 GC在 HotSpot VM 5.0里 ...
- HotSpot VM垃圾收集器
最常用的HotSpot VM垃圾收集器是分代垃圾收集.该方案是基于两个观察事实. 大多数分配对象的存活时间很短. 存活时间久的对象很少引用存活时间短的对象. 上述两个观察事实统称为弱分代假设(Weak ...
- 关于Linux x64 Oracle JDK7u60 64-bit HotSpot VM 线程栈默认大小问题的整理
JVM线程的栈默认大小,oracle官网有简单描述: In Java SE 6, the default on Sparc is 512k in the 32-bit VM, and 1024k in ...
- c语言 指针与地址的区别
指针由两部分组成,指针的类型和指针的值(也就是变量的地址). 指针和地址的区别: 地址只是一堆十六进制的字符,对应着内存条的某段内存, 而指针本身有地址,指针的值也是一个地址,指针本身还有类型,这与单 ...
随机推荐
- CentOS7环境编译python3.9版本pjsua
环境:CentOS 7.6_x64 Python版本 :3.9.12 pjsip版本:2.13 一.背景描述 pjsip地址:https://www.pjsip.org/ GitHub地址:https ...
- 介绍Vue router的history模式以及如何配置history模式
引言 Vue router给我们提供了两种路由模式,分别是hash模式和history模式.其中默认是使用hash模式,即URL中带有一个#符号,但是处于业务或个人喜爱的差别,Vue router也提 ...
- 即构SDK7月迭代:新增支持按通道设置延迟模式,让卡顿大大减少
即构SDK 7月迭代如期而至,本月SDK更新主要增加了按推流通道设置延迟模式,大大减少了直播卡顿:媒体本地录制新增AAC 格式,可生成更小的录制文件,更易于上传.此外还有多项功能的优化,让用户获得更好 ...
- Hexo博客Next6.0版本主题配置(站内搜索、新建404界面、静态资源压缩、底部信息隐藏、各版块透明度修改、字数统计、推荐阅读、博文置顶、阅读进度、在线评论、运行时间)
新建404界面 在站点根目录下,输入hexo new page 404,在默认Hexo站点下/source/404/index.md 打开新建的404界面,编辑属于自己的404界面,可以显示腾讯公益4 ...
- Centos静默安装Oracle11G
环境准备 Oracle 11gR2 64位 Linux版安装包 linux.x64_11gR2_database_1of2.zip linux.x64_11gR2_database_2of2.zip ...
- Vue-treeselect 实现下拉树懒加载,末节点不要箭头
项目需要,可选择的下拉树,由于数据过多,显示要有层级感,所以使用了懒加载模式 vue-treeselect官网:https://www.vue-treeselect.cn/ 1.前端代码 1.下载依赖 ...
- mysql怎样实现不重复插入数据
mysql使用用insert往数据表中插入数据时,为了不重复插入数据,往往先查询一下该条数据是否已经存在,若不存在才进行插入操作. 而使用 insert if not exists语句,就不需重复做上 ...
- pandas: dataframe转字典,并设置key
解决方案 res = df_by_monthly.set_index('recruit_resign_month').T.to_dict('list') print(res) 上述代码是转为列表形式 ...
- pandas 根据内容匹配并获取索引
bool = ExcelDataStr.str.contains("Item No./Customer/Saler") # 初始位置:initial position, 终位置:e ...
- bzip2: (stdin) is not a bzip2 file.
用tar -zxvf dir.tar.gz命令解压即可.