C++在HotSpot VM中一种巧妙的内存管理方式

c++,hotspot,vm,一种,巧妙,内存,管理,方式 · 浏览次数 : 20

小编点评

**设计设计:** * **RelocationHolder** 是一个独立的类,用于管理特定类型的重定位数据。 * **DataRelocation** 和 **CallRelocation** 是子类,分别用于存储数据和执行重定位操作。 * **Relocation** 是 **RelocationHolder** 的抽象,它提供了一个统一的接口,使不同的重定位类可以实现相同的功能。 **工厂方法:** * **spec** 方法用于创建不同的 **RelocationHolder** 实例,根据不同的重定位数据类型。 * **relocate** 方法用于从 **RelocationHolder** 中获取一个重定位实例,并执行相应的重定位操作。 **主要特点:** * 使用 **RelocationHolder** 缓存重定位数据,从而减少内存分配和处理时间。 * 使用工厂方法简化了创建不同类型的重定位对象。 * 每个重定位类仅创建一次 **RelocationHolder**,并在其范围内共享该实例。 **优点:** * 减少内存分配和处理时间。 * 简化了重定位操作。 * 利用工厂方法,可以简化创建不同类型的重定位对象。 **缺点:** * **RelocationHolder** 类的内存分配可能会影响性能。 * 每个 **Relocation** 对象都从 **RelocationHolder** 中创建,可能会导致内存碎片。

正文

在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中一种巧妙的内存管理方式相似的内容:

C++在HotSpot VM中一种巧妙的内存管理方式

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

C++的模板类在HotSpot VM中的应用

模板是c++的一种特性,允许函数或者类通过泛型(generic types)的形式表现或者运行。模板可以使得函数或类在对应不同的类型(types)的时候正常工作,而无需为每一种类型分别写一份代码。 在HotSpot VM中定义了一些模板类,有了这些模板类,我们就可以和Java一样进行泛型编程。Hot

C++ 重载运算符在HotSpot VM中的应用

C++支持运算符重载,对于Java开发者来说,这个可能比较陌生一些,因为Java不支持运算符重载。运算符重载本质上来说就是函数重载。下面介绍一下HotSpot VM中的运算符重载。 1、内存分配与释放 在C++中可以通过new运算符创建一个C++的类实例,这个操作实际上上包含了如下3个步骤: 调用o

C++指针和地址偏移在HotSpot VM中的应用

在前面我们介绍过new运算符,这个操作实际上上包含了如下3个步骤: 调用operator new的标准库函数。此函数会分配一块内存空间以便函存储相应类型的实例; 调用相应类的构造函数; 返回一个指向该对象的指针。 在第一步中,其实我们可以自己写个operator new函数对标准库函数进行重载,通常

C++的动态分派在HotSpot VM中的重要应用

众所周知,多态是面向对象编程语言的重要特性,它允许基类的指针或引用指向派生类的对象,而在具体访问时实现方法的动态绑定。C++ 和 Java 作为当前最为流行的两种面向对象编程语言,其内部对于多态的支持对于单继承的实现非常类似。 首先来体现一下C++的动态分派,如下: class Base1{ pub

C++ RAII在HotSpot VM中的重要应用

在HotSpot VM中,RAII对内存资源的管理和释放、明确定义范围锁及记录重要信息等方面起到了非常重要的作用。

C++的extern关键字在HotSpot VM中的重要应用

extern关键字有两个用处: (1)extern在C/C++语言中表示函数和全局变量作用范围(可见性)的关键字,这个关键字会告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。 (2)在C++中引用C语言中的函数和变量,在包含C语言头文件时,需要使用extern "C"来处理。 1、ext

深度解读《深度探索C++对象模型》之C++虚函数实现分析(三)

本系列深入分析编译器对于C++虚函数的底层实现,最后分析C++在多态的情况下的性能是否有受影响,多态究竟有多大的性能损失。

深度解读《深度探索C++对象模型》之C++虚函数实现分析(二)

本系列深入分析编译器对于C++虚函数的底层实现,最后分析C++在多态的情况下的性能是否有受影响,多态究竟有多大的性能损失。

深度解读《深度探索C++对象模型》之C++虚函数实现分析(一)

本系列深入分析编译器对于C++虚函数的底层实现,最后分析C++在多态的情况下的性能是否有受影响,多态究竟有多大的性能损失。