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

c++,重载,运算符,hotspot,vm,应用 · 浏览次数 : 37

小编点评

**C++运算符重载** C++支持运算符重载,允许在编译器编译时为特定运算符分配不同的实现。对于 Java 开发人员来说,由于 Java 不支持运算符重载,这可能比较陌生。 **内存分配与释放在C++中** C++ 中可以使用 `new`运算符创建对象,并使用 `delete`运算符释放对象的内存空间。由于 C++ 没有 Java 的 GC托管技术,因此分配出来的内存时刻要手动释放。 **句柄** 句柄是一种间接操作实例的机制,可以用于间接操作实例,让 GC 能够集中扫描到栈中引用的 Java 对象。 **重载运算符** C++ 可以重载 `new` 和 `delete` 运算符,通过在 `class` 中定义这些方法,为实例提供不同的内存分配和释放逻辑。 **示例** ```cpp // 重载 new 运算符 void* Klass::operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw() { // 在元数据区分配内存空间 void* x = Metaspace::allocate(...); return x; } // 重载 delete 运算符 void Klass::operator delete(void* x) { // 在元数据区释放内存 Metaspace::deallocate(x); } ```

正文

C++支持运算符重载,对于Java开发者来说,这个可能比较陌生一些,因为Java不支持运算符重载。运算符重载本质上来说就是函数重载。下面介绍一下HotSpot VM中的运算符重载。

1、内存分配与释放

在C++中可以通过new运算符创建一个C++的类实例,这个操作实际上上包含了如下3个步骤:

  1. 调用operator new的标准库函数。此函数会分配一块内存空间以便函存储相应类型的实例。
  2. 调用相应类的构造函数
  3. 返回一个指向该对象的指针

同样,可以delete运算符释放对应的内存,实际执行如下2个步骤:

  1. 调用相应类的析构函数
  2. 调用operator delete标准库函数释放内存。

由于C++没有Java的GC托管技术,所以分配出来的内存时刻要惦记着释放,这是一件非常不容易的事情。通常的做法是,内存申请和释放集中到一个地方管理,所以才会有Metaspace或Arena这些相对复杂一些的内存管理机制。

有了我们自己设计的内存管理机制后,就可以重载new运算符,让实例从特定的内存空间中申请和释放内存了,例如HotSpot VM在Klass类中重载了new运算符:

void* Klass::operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw() {
  // 在元数据区分配内存空间
  void* x = Metaspace::allocate( 
				 loader_data,
				 word_size,
				 false,   /*read_only*/
				 MetaspaceObj::ClassType,
				 CHECK_NULL
			 );
  return x;
}

在使用new关键字创建Klass或子类的实例时,都会调用Metaspace::allocate()函数从元数据区分配内存;在Klass类中,我们没有看到重载delete运算符,因为删除一个类并没有那么简单,需要借助GC来完成。元数据区具体管理内存的办法,以及分配和释放的逻辑可参看《深入剖析Java虚拟机:源码剖析与实例详解》中的8.2节。
在HotSpot VM中重载new和delete运算符的地方非常多,不过oop并不是这样做的,这应该是考虑到它相对复杂的内存分配逻辑和初始化过程吧。

2、句柄

关于句柄,我在之前 第2.7篇-操作句柄Handle 详细介绍过它的作用。句柄要间接操作实例,让GC能够集中扫描到栈中引用到的Java对象。
句柄的相关定义如下:

class Handle {
private:
    oop *_handle; // oop的类型为oopDesc*

protected:
    oop obj() const {
        return _handle == NULL ? (oop) NULL : *_handle;
    }
    oop non_null_obj() const {
        return *_handle;
    }

public:
    // 重载了()、->和==运算符
    oop operator()() const { return obj(); }
    oop operator->() const { return non_null_obj(); }
    bool operator==(oop o) const { return obj() == o; }
    bool operator==(const Handle &h) const { return obj() == h.obj(); }
};

句柄中重载了()、->和==运算符,我们可以这样操作:

oop obj1 = ...;
// 将对象封装为句柄
Handle h1(obj1); 

// 获取被封装的对象,会调用到operator()()函数,这个函数返回*_handle
oop obj2 = h1();
// 直接调用oop中定义的相关函数,会调用到operator->()函数,
// 在这个函数中获取_handle值后调用_handle->print()函数 
h1->print();

这大大简化了相关操作的简洁性,操作句柄就感觉和操作oop是一样的效果

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

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

与C++ 重载运算符在HotSpot VM中的应用相似的内容:

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

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

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

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

用现代C++写一个python的简易型list

std::variant介绍:en.cppreference.com/w/cpp/utility/variant 通过泛型模板(仅提供了int, double, string三种类型的存储),实现了append, pop, front, back, size等方法,并且通过重载运算符实现了对负数索引

C++多态与虚拟:运算符重载(Operator Overloading)

运算符重载:与function overloading异曲同工的是,C++提供所谓的Operator overloading。所谓operators是像 +(加)-(減)*(乘)/(除)>>(位右移)<<(位左移)之类的符号,代表一种动作。 面对operators,我们应该把他想像是一种函数,只不过

C#.Net筑基-运算符Family

C#运算符 内置了丰富的运算符操作类型,使用方便,极大的简化了编码,同时还支持多种运算符重载机制,让自定义的类型也能支持运算符行为。

C++判断当前程序是否运行在Windows展台(Kiosk)模式下

Windows有一个展台(Kiosk)模式。展台模式可以使Windows作为数字标牌进行使用。具体请参考Windows 展台 配置完展台模式,重启设备后,Windows会以全屏的方式运行展台应用,无法进入桌面。有点类似iPhone中的引导者模式。此时我们自己的应用如果设置了开机自启,也会运行,但是会

C++ ASIO 实现异步套接字管理

Boost ASIO(Asynchronous I/O)是一个用于异步I/O操作的C++库,该框架提供了一种方便的方式来处理网络通信、多线程编程和异步操作。特别适用于网络应用程序的开发,从基本的网络通信到复杂的异步操作,如远程控制程序、高并发服务器等都可以使用该框架。该框架的优势在于其允许处理多个并发连接,而不必创建一个线程来管理每个连接。最重要的是ASIO是一个跨平台库,可以运行在任何支持C++

C#实现多线程的几种方式

前言 多线程是C#中一个重要的概念,多线程指的是在同一进程中同时运行多个线程的机制。多线程适用于需要提高系统并发性、吞吐量和响应速度的场景,可以充分利用多核处理器和系统资源,提高应用程序的性能和效率。 多线程常用场景 CPU 密集型任务. I/O 密集型任务. 并发请求处理. 大数据处理等. 什么是

C#的奇技淫巧:利用WinRM来远程操控其他服务器上的进程

前言:有时候远程服务器的进程你想偷偷去围观一下有哪些,或者对一些比较调皮的进程进行封杀,或者对一些自己研发的服务进行远程手动启动或者重启等,又不想打开远程桌面,只想悄咪咪地执行,那也许下面的文章会对你有启发。 前提条件 确保远程服务器(服务端)已启用WinRM。在远程服务器上运行以下命令可以启用和配

C#的重载决策

重载是许多编程语言支持的特性。所谓重载,就是指可以定义多个名称相同但参数(个数、类型和顺序)不同的方法(函数)。先来看一个例子: ```c# void Main() { char cvalue = 'a'; male m = new male(); m.write(cvalue); } class