在HotSpot VM中定义了一个Relocation类及相关的子类,可以通过这些类操作不同的重定位数据,如在CodeCache中读写这些数据。这些类需要的内存很小,但是不同的类需要的内存大小又不一样,所以做了如下的设计:

#include <cstdlib>
#include "iostream" class Relocation; class RelocationHolder {
friend class Relocation;
private :
enum {
_relocbuf_size = 5
};
// 总共分配了5 * 8 = 40 字节大
// 小的内存用来存储Relocation
void* _relocbuf[ _relocbuf_size ];
public :
Relocation* reloc() const {
return (Relocation*) &_relocbuf[0];
}
}; class Relocation {
public:
void *operator new(size_t size, const RelocationHolder &holder) {
// 由于Relocation是RelocationHolder的友元,
// 所以能访问其私有的_relocbuf数据
if (size > sizeof(holder._relocbuf)) {
std::cerr << "error" << std::endl;
}
return holder.reloc();
}
// 虚函数,子类可重写,这样能进行动态分派
virtual void pack_data_to() {}
}; class DataRelocation : public Relocation {
private:
int _data; // 具体数据
public:
DataRelocation(int data){
_data = data;
} virtual void pack_data_to() {
std::cout << "DataReloction::pack_data_to()" << std::endl;
}
}; class CallRelocation : public Relocation {
private:
u_char* _call_pc; // 具体数据
public:
CallRelocation(u_char* call_pc) {
_call_pc = call_pc;
} virtual void pack_data_to() {
std::cout << "CallRelocation::pack_data_to()" << std::endl;
}
};

其中的RelocationHolder是一个具体的Relocation的持有者。DataRelocation和CallRelocation代表了不同的重定位数据,可以调用对应的pack_data_to()函数按一定的规则将相应的数据写入CodeCache中。

下面看具体的使用,如下:

int main() {
// 在栈上分配内存
RelocationHolder rh; // 使用RelocationHolder中的_relocbuf数组占用的内存为DataRelocation
// 分配内存
u_char *addr = NULL;
CallRelocation *dr = new(rh) CallRelocation(addr);
dr->pack_data_to(); // DataRelocation操作完成后,重用RelocationHolder中_relocbuf的
// 内存
DataRelocation *cr = new(rh) DataRelocation(100);
cr->pack_data_to(); return 0;
}

RelocationHolder中的_relocbuf数组有固定的40字节内存,这些内存都分配在栈上,而DataRelocation或CallRelocation虽然需要的内存大小不同,但是都小于40字节,所以当CallRelocation使用完后,DataRelocation又可以重用这一部分栈内存。虽然使用new关键字创建了2个对象,但是分配的内存都在栈上,不需要释放。当函数返回时,对象会自动失效。

运行后的结果如下:

DataReloction::pack_data_to()
CallRelocation::pack_data_to()

如上的方法已经能满足一部分需求,但是使用起来不方便,首先需要找一个RelocationHolder,然后还需要自己创建一个对应的Relocation实例出来。为了让程序用起来更方便,也更优雅一些,HotSpot VM又增加了一些设计,提供了工厂方法,改造后的代码如下:

class Relocation {
public:
static RelocationHolder newHolder() {
// 调用默认的构造函数,生成一个在栈上分配内存的
// RelocationHolder类型的对象
// 注意,这里创建的对象在调用完函数后会
// 失效,返回的是一个通过拷贝构造函数
// 拷贝到栈上的一个临时对象
return RelocationHolder();
} void *operator new(size_t size, const RelocationHolder &holder) {
// 由于Relocation是RelocationHolder的友元,
// 所以能访问其私有的_relocbuf数据
if (size > sizeof(holder._relocbuf)) {
std::cerr << "error" << std::endl;
}
return holder.reloc();
} virtual void pack_data_to() {}
}; class DataRelocation : public Relocation {
private:
int _data;
public:
DataRelocation(int data){
_data = data;
}
static RelocationHolder spec(int data) {
RelocationHolder rh = newHolder();
new(rh) DataRelocation(data);
return rh;
} virtual void pack_data_to() {
std::cout << "DataReloction::pack_data_to()" << std::endl;
}
}; class CallRelocation : public Relocation {
private:
u_char* _call_pc;
public:
CallRelocation(u_char* call_pc) {
_call_pc = call_pc;
}
static RelocationHolder spec(u_char* call_pc) {
RelocationHolder rh = newHolder();
new(rh) CallRelocation(call_pc);
return rh;
} virtual void pack_data_to() {
std::cout << "CallRelocation::pack_data_to()" << std::endl;
}
};

RelocationHolder类不需要改动,主要是为CallRelocation和DataRelocation增加了工厂方法spec(),同样使用的是栈上分配的内存,不需要释放,使用时只需要这样:

void relocate(RelocationHolder const& spec) {
Relocation* reloc = spec.reloc();
// 处理重定位相关数据
reloc->pack_data_to();
}; int main() {
// 收集重定位相关数据
u_char *addr = NULL;
RelocationHolder r1 = CallRelocation::spec(addr);
relocate(r1); RelocationHolder r2 = DataRelocation::spec(100);
relocate(r2); return 0;
}

这样看起来是不是要比之前更加简洁了呢?在一个函数中收集数据,在另外的函数中处理数据。  

本人最近准备出一个手写Hotspot VM的课程,超级硬核,从0开始写HotSpot VM,将HotSpot VM所有核心的实现全部走一遍,如感兴趣,加我速速入群。

已加群的不要重复加。

群里可讨论虚拟机和Java性能剖析与故障诊断等话题,欢迎加入。

  

C++在HotSpot VM中一种巧妙的内存管理方式的更多相关文章

  1. JVM详解之:HotSpot VM中的Intrinsic methods

    目录 简介 什么是Intrinsic Methods 内置方法的特点 多样性 兼容性 java语义的扩展 Hotspot VM中的内置方法 intrinsic方法和内联方法 intrinsic方法的实 ...

  2. HotSpot VM 中的JIT分类

    在HotSpot VM中内嵌有两个JIT编译器,分别为Client Compiler和Server Compiler,但大多数情况下我们简称为C1编译器和C2编译器.开发人员可以通过如下命令显式指定J ...

  3. Android中三种超实用的滑屏方式汇总(转载)

    Android中三种超实用的滑屏方式汇总   现如今主流的Android应用中,都少不了左右滑动滚屏这项功能,(貌似现在好多人使用智能机都习惯性的有事没事的左右滑屏,也不知道在干什么...嘿嘿),由于 ...

  4. JavaScript中四种不同的属性检测方式比较

    JavaScript中四种不同的属性检测方式比较 1. 用in方法 var o = {x:1}; "x" in o; //true "y" in o; //fa ...

  5. JavaScript 中 4 种常见的内存泄露陷阱

    了解 JavaScript 的内存泄露和解决方式! 在这篇文章中我们将要探索客户端 JavaScript 代码中常见的一些内存泄漏的情况,并且学习如何使用 Chrome 的开发工具来发现他们.读一读吧 ...

  6. spring mvc中几种获取request对象的方式

    在使用spring进行web开发的时候,优势会用到request对象,用来获取访问ip.请求头信息等 这里收集几种获取request对象的方式 方法一:在controller里面的加参数 public ...

  7. Swift中的可选链与内存管理(干货系列)

    干货之前:补充一下可选链(optional chain) class A { var p: B? } class B { var p: C? } class C { func cm() -> S ...

  8. 高端内存映射之vmalloc分配内存中不连续的页--Linux内存管理(十九)

    1 内存中不连续的页的分配 根据上文的讲述, 我们知道物理上连续的映射对内核是最好的, 但并不总能成功地使用. 在分配一大块内存时, 可能竭尽全力也无法找到连续的内存块. 在用户空间中这不是问题,因为 ...

  9. C++中的垃圾回收和内存管理

    最开始的时候看到了许式伟的内存管理变革系列,看到性能测试结果的时候,觉得这个实现很不错,没有深入研究其实现.现在想把这个用到自己的一个项目中来,在linux下编译存在一些问题,所以打算深入研究一下. ...

  10. cocos2dx中的内存管理方式

    转载:http://www.cocoachina.com/bbs/read.php?tid=195219 今天看了一下cocos2dx的内存管理机制,有些地方不太好理解搞了挺长的时间,现在感觉自己理解 ...

随机推荐

  1. 【TVM模型编译】1. onnx2relay.md

    上一篇介绍了onnx模型在tvm中优化的总体流程. 在这一篇中,介绍onnx模型到relay模型的转换流程,主要涉及了以下几个方面: onnx算子到relay算子转换 relay算子实现 这一篇介绍o ...

  2. 文献精读1:SpikTransformer

    Spikformer code source(pku):GitHub - ZK-Zhou/spikformer: ICLR 2023, Spikformer: When Spiking Neural ...

  3. 华为云GaussDB亮相2023可信数据库发展大会,荣获三项评测证书!

    摘要:2023可信数据库发展大会上,华为云数据库服务产品部总经理苏光牛围绕华为云GaussDB的产品能力和实践进行了分享 本文分享自华为云社区<华为云GaussDB亮相2023可信数据库发展大会 ...

  4. VSCode中打开NodeJS项目自动切换对应版本的配置

    这几年搞了不少静态站点,有的是Hexo的,有的是VuePress的.由于不同的主题对于NodeJS的版本要求不同,所以本机上不少NodeJS的版本. 关于如何管理多个NodeJS版本,很早之前就写过用 ...

  5. 从钢铁行业数字化管控与超自动化融合,看华为WeAutomate能力进化

    文/王吉伟 钢铁行业的数字化转型,历来都是值得探讨的热点话题. 2022年,我国粗钢产量10.13亿吨,占据了全球钢铁供给市场的半壁江山. 这组数据可谓非常抢眼,但仍旧难掩诸多企业的各种经营问题. 钢 ...

  6. docker 安装redis 6.0.8哨兵集群(一主两从三哨兵)

    准备三台主机并且安装了docker 192.168.31.132 192.168.31.134 192.168.31.144 linux 版redis6.0.8 下载 下载地址:https://dow ...

  7. 从逻辑门到 CPU

    目的,造一个很简单的,概念上的 CPU,虽然简单,但是是五脏俱全的 CPU 从最基础的逻辑门开始造,零基础可以看 制造基本武器:与门.非门.或门 现在计算机都是二进制,那二进制是一开始就能想到的吗?显 ...

  8. PTA 21级数据结构与算法实验5—树和二叉树

    目录 7-1 还原二叉树 7-2 朋友圈 7-3 修理牧场 7-4 玩转二叉树 7-5 根据后序和中序遍历输出先序遍历 7-6 完全二叉树的层序遍历 7-7 列出叶结点 7-8 部落 7-9 建立与遍 ...

  9. 【技术积累】Vue中的核心概念【四】

    Vue的生命周期 Vue中的生命周期是指组件从创建到销毁的整个过程中,会触发一系列的钩子函数 Vue2中的生命周期 Vue2中的生命周期钩子函数是在组件的不同阶段执行的特定函数.这些钩子函数允许开发者 ...

  10. Redis 集群偶数节点跨地域部署之高可用测试

    笔者目前所在公司存在多套 Redis 集群: A 集群 主 + 从 共 60 个分片,部署在 3 + 3 台物理机上,每台机器各承载 10 个端口 主库 30 个端口在广州,从库 30 个端口在中山 ...