第十章:大内存的申请和释放

news/2025/2/2 20:47:22 标签: c++, 内存池

目录

第一节:函数修改

        1-1.ConcurrentAlloc.h

        1-2.Common.h

        1-3.PageCache.cpp

第二节:测试

第三节:结语


        大内存的思路是将其以一页为对齐数,申请一个为切分的span,这种span在pc就有,所以直接到pc中申请,释放时也直接还给pc,不需要经过tc和cc。

第一节:函数修改

        1-1.ConcurrentAlloc.h

        该文件的函数ConcurrentAllocate增加处理大内存的代码:

// 线程申请私有的tc+申请size字节内存
static void* ConcurrentAlloc(size_t size) {
	if (pTlsTC == nullptr) {
		static FixedLengthMemoryPool<ThreadCache> cmpool;
		pTlsTC = cmpool.New();
	}

	//----------------------------------------------------------------
	// 处理大内存
	if (size > MAX_BYTES) {
		size_t alignSize = SizeClass::RoundUp(size);
		size_t npage = alignSize >> PAGE_SHIFT;			// 得到页数

		// 获得足够大的span
		PageCache::GetInst()->_mtx.lock();
		Span* span = PageCache::GetInst()->NewSpan(npage);
		span->_objSize = alignSize;
		PageCache::GetInst()->_mtx.unlock();

		void* ptr = (void*)((span->_pageId) << PAGE_SHIFT);
		return ptr;
	}
	//----------------------------------------------------------------

	return pTlsTC->Allocate(size);
}

        函数ConcurrentFree新增如下代码:

static void ConcurrentFree(void* ptr) {
	assert(pTlsTC);
	Span* span = PageCache::GetInst()->MapObjToSpan(ptr);
	size_t size = span->_objSize;

	//----------------------------------------------------------------
	// 释放大内存
	if (size > MAX_BYTES) {
		PageCache::GetInst()->_mtx.lock();
		// 直接将该span还给pc
		PageCache::GetInst()->ReleaseSpanToPageCache(span);
		PageCache::GetInst()->_mtx.unlock();
		return;
	}
	//----------------------------------------------------------------

	pTlsTC->Deallocate(ptr, size);
}

        1-2.Common.h

        需要修改的是SizeClass中的RoundUp函数,之前对于大内存的处理是报错,需要修改成以一页为大小进行对齐:

// size对齐后的字节数
static size_t RoundUp(size_t size) {
	if (size <= 128)
		return _RoundUp(size, 8);
	else if (size <= 1024)
		return _RoundUp(size, 16);
	else if (size <= 8 * 1024)
		return _RoundUp(size, 128);
	else if (size <= 64 * 1024)
		return _RoundUp(size, 1024);
	else if (size <= 256 * 1024)
		return _RoundUp(size, 8 * 1024);
	else { // 大于MAX_BYTES的大块内存
		return _RoundUp(size, 1 << PAGE_SHIFT); // 修改处
	}
}

        再在Common.h中添加一个名为SystemFree的函数,它的作用是如果还回来的内存大于128页,那么pc放不下,直接还给堆:

// 向堆还空间
static void SystemFree(void* ptr) {
	VirtualFree(ptr, 0, MEM_RELEASE);
}

        1-3.PageCache.cpp

        修改NewSpan函数,如果申请的内存大于128页,那么直接向堆获取,直接返回:

Span* PageCache::NewSpan(size_t npage) {
	assert(npage > 0);

	//----------------------------------------------------------------
	// 大于128页直接从堆申请
	if (npage > NPAGE - 1) {
		void* ptr = SystemAlloc(npage);
		Span* span = _fLengthMemPool.New();
		span->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
		span->_n = npage;
		_idSpanMap.set(span->_pageId,span);
		return span;
	}
	//----------------------------------------------------------------
    // 其他内容不变......
}

        修改ReleaseSpanToPageCache函数,如果释放的内存大于128页就调用SystemFree函数将其直接还给堆:

void PageCache::ReleaseSpanToPageCache(Span* span) {
	//----------------------------------------------------------------
	// 大于128页直接还给堆
	if (span->_n > NPAGE - 1) { 
		void* ptr = (void*)(span->_pageId << PAGE_SHIFT);
		SystemFree(ptr);
		_fLengthMemPool.Delete(span);
		return;
	}
	//----------------------------------------------------------------
    // 其他内容不变......
}

        这样代码就修改完毕了。

第二节:测试

        申请各种大小的大内存,只要没报错代码就可以了:

std::atomic<size_t> poolCostTime = 0;
std::atomic<size_t> mallocCostTime = 0;
size_t nTimes = 10;

void _ConWithMalloc() {
	std::vector<void*> vPtr1;
	std::vector<void*> vPtr2;
	vPtr1.reserve(nTimes);
	vPtr2.reserve(nTimes);
	size_t begin1 = clock();
	for (size_t i = 0; i < nTimes; i++) {
		vPtr1.push_back(ConcurrentAlloc(8*129 << PAGE_SHIFT)); // 修改其中的值测试不同大小的内存申请
	}
	size_t end1 = clock();

	size_t begin2 = clock();
	for (size_t i = 0; i < nTimes; i++) {
		ConcurrentFree(vPtr1[i]);
	}
	size_t end2 = clock();
	 poolCostTime += (end1 - begin1) + (end2 - begin2);

	begin1 = clock();
	for (size_t i = 0; i < nTimes; i++) {
		vPtr2.push_back(malloc(8 * 56 << PAGE_SHIFT));
	}
	end1 = clock();

	begin2 = clock();
	for (size_t i = 0; i < nTimes; i++) {
		free(vPtr2[i]);
	}
	end2 = clock();
	mallocCostTime += (end1 - begin1) + (end2 - begin2);
}

void ConWithMalloc(size_t nWorks) {
	// nWorks个线程,每个线程执行nTimes次申请和释放内存
	std::vector<std::thread> vThread(nWorks);
	for (size_t k = 0; k < nWorks; k++) {
		vThread[k] = std::thread(_ConWithMalloc);
	}
	for (auto& t : vThread)
		t.join();
	 std::cout << poolCostTime << "   " << mallocCostTime << std::endl;

}


int main() {
	for (size_t i = 0; i < 100; i++)
		ConWithMalloc(4);

	return 0;
}

第三节:结语

        这样一个简化的高并发内存池就完成了,这是tcmalloc的简化版本,学习项目的主要目的还是学习大佬的代码思路。

        这是我学习的第一个项目,这种内存相关的bug真的是不好找,有时候一个bug要找几个小时,但是我仍然还是坚持下来了。

        我学习到了找bug的技巧、大佬的代码思路,这个项目完成后,我要需要马不停蹄的学习下一个项目了。

        路漫漫其修远兮,吾将上下而求索。


http://www.niftyadmin.cn/n/5840288.html

相关文章

一文讲解Java中的ArrayList和LinkedList

ArrayList和LinkedList有什么区别&#xff1f; ArrayList 是基于数组实现的&#xff0c;LinkedList 是基于链表实现的。 二者用途有什么不同&#xff1f; 多数情况下&#xff0c;ArrayList更利于查找&#xff0c;LinkedList更利于增删 由于 ArrayList 是基于数组实现的&#…

【Linux】opencv在arm64上提示找不到libjasper-dev

解决opencv在arm64上提示找不到libjasper-dev的问题。 本文首发于❄慕雪的寒舍 问题说明 最近我在尝试编译opencv&#xff0c;安装依赖项libjasper1和libjasper-dev的时候就遇到了这个问题。在amd64平台上&#xff0c;我们可以通过下面的命令安装&#xff08;ubuntu18.04&…

FreeRTOS学习笔记2:FreeRTOS的基础知识

1.FreeRTOS介绍 FreeRTOS是一个免费的嵌入式实时操作系统&#xff0c;同时它在市面上也是一款主流的操作系统&#xff0c;是工作上必不可少的技能。它具有以下六种特点&#xff1a; 1.免费开源&#xff1a;在商业产品中使用&#xff0c;无潜在商业风险&#xff0c;无需担心。 2…

NIST的 临床质量指标的简介

啥是NIST&#xff1f; 美国国家标准与技术研究院&#xff08;National Institute of Standards and Technology&#xff0c;NIST&#xff09;直属美国商务部&#xff0c;从事物理、生物和工程方面的基础和应用研究&#xff0c;以及测量技术和测试方法方面的研究&#xff0c;提…

leetcode 2563. 统计公平数对的数目

题目如下 数据范围 显然数组长度最大可以到10的5次方n方的复杂度必然超时&#xff0c;阅读题目实际上就是寻找两个位置不同的数满足不等式即可(实际上i j无所谓是哪个 我们只要把位置小的想成i就行)。 按照上面的思路我们只需要排序数组然后从前往后遍历数组然后利用二分查找…

操作系统知识速记:死锁

操作系统知识速记&#xff1a;死锁 什么是死锁&#xff1f; 死锁是指两个或多个进程因争夺资源而造成的一种相互等待的状态&#xff0c;进程间形成循环等待&#xff0c;导致所有进程均无法继续执行。通常情况下&#xff0c;死锁的发生有以下四个必要条件&#xff1a; 互斥条…

Java小白入门教程:封装、继承、多态、重载、重写、抽象、接口

目录 一、封装&#xff08;Encapsulation&#xff09; 基本语法举例 真实实例 二、继承&#xff08;Inheritance&#xff09; 基本语法举例 真实实例 三、多态&#xff08;Polymorphism&#xff09; 基本语法举例 真实实例 四、重载&#xff08;Overloading&#xff0…

蓝桥杯算法笔记|差分学习

&#xff01;前情回顾 前缀和18437蓝桥账户中心 练习代码&#xff1a; #include <iostream> using namespace std; int main() {// 请在此输入您的代码int n,q;cin>>n>>q;int a[n];for(int i0;i<n;i){cin>>a[i];}int sum[n];sum[0]a[0];for(int …