当前位置: 首页 > news >正文

重庆永川网站建设公司门户营销型网站搭建

重庆永川网站建设公司,门户营销型网站搭建,怎样说服公司做网站,济南网站seo顾问文章目录 C | 定长内存池 | 对象池一、内存池的引入二、代码中的内存池实现 - ObjectPool类#xff08;一#xff09;整体结构#xff08;二#xff09;内存分配 - New函数#xff08;三#xff09;内存回收 - Delete函数 三、内存池在TreeNode示例中的性能测试演示四、脱… 文章目录 C | 定长内存池 | 对象池一、内存池的引入二、代码中的内存池实现 - ObjectPool类一整体结构二内存分配 - New函数三内存回收 - Delete函数 三、内存池在TreeNode示例中的性能测试演示四、脱离malloc直接在堆上按页申请空间五、总结一内存池的优势二代码的思考 C | 定长内存池 | 对象池 在C编程的世界里内存管理是一个至关重要的话题。今天我们就来深入研究一下基于下面这段代码的内存池概念。 先上代码 #includeiostream #includevector #include time.h using std::cout; using std::endl;//定长内存池一次申请N大小的空间 //templatesize_t N //class ObjectPool { //};templateclass T class ObjectPool { public:T* New() {T* obj nullptr;if (_freelist) //_freelist非空说明有还回来的内存优先使用还回来的内存//可以使用类似无头结点链表的头删{void* next *(void**)_freelist;obj (T*)_freelist;_freelist next;}else{if (_remainbytes sizeof(T)) {_remainbytes 128 * 1024;_memory (char*)malloc(128 * 1024);if (_memory nullptr) {throw std::bad_alloc();}}obj (T*)_memory; //申请出去的新的空间的起始位置//_memory sizeof(T); //新的空间被申请后剩余空间的起始位置size_t objsize sizeof(T) sizeof(void*) ? sizeof(void*) : sizeof(T);_memory objsize;_remainbytes - sizeof(T); //剩余的空间}//定位new显式调用T的构造函数初始化new(obj)T;return obj;}void Delete(T* obj) {//显式调用析构函数清理对象obj-~T();//就相当于无头结点链表的头插*(void**)obj _freelist;_freelist *(void**)obj;} private://void* _memory;//为了方便地址上的加减运算使用char类型char类型占一个字节char* _memory nullptr; //指向大块内存的指针void* _freelist nullptr; //指向待释放的空间size_t _remainbytes 0; // 大块内存在切分过程中剩余字节数 };struct TreeNode//二叉树节点 {int _val;TreeNode* _left;TreeNode* _right;TreeNode():_val(0), _left(nullptr), _right(nullptr){} };void TestObjectPool() {// 申请释放的轮次const size_t Rounds 5;// 每轮申请释放多少次const size_t N 100000;std::vectorTreeNode* v1;v1.reserve(N);size_t begin1 clock();for (size_t j 0; j Rounds; j){for (int i 0; i N; i){v1.push_back(new TreeNode);}for (int i 0; i N; i){delete v1[i];}v1.clear();}size_t end1 clock();std::vectorTreeNode* v2;v2.reserve(N);ObjectPoolTreeNode TNPool;size_t begin2 clock();for (size_t j 0; j Rounds; j){for (int i 0; i N; i){v2.push_back(TNPool.New());}for (int i 0; i N; i){TNPool.Delete(v2[i]);}v2.clear();}size_t end2 clock();cout new cost time: end1 - begin1 endl;cout object pool cost time: end2 - begin2 endl; }int main() {TestObjectPool();return 0; }一、内存池的引入 在很多C程序中频繁地进行内存的分配和释放例如使用new和delete操作符可能会带来性能上的开销尤其是在处理大量小对象时。内存池Memory Pool技术应运而生它就像是一个预先准备好的内存资源仓库能够更高效地管理内存的分配和回收。 二、代码中的内存池实现 - ObjectPool类 一整体结构 我们来看代码中的ObjectPool类这是一个模板类templateclass T这意味着它可以为不同类型的对象提供内存池服务。 templateclass T class ObjectPool { public:T* New();void Delete(T* obj); private://void* _memory;//为了方便地址上的加减运算使用char类型char类型占一个字节char* _memory nullptr; //指向大块内存的指针void* _freelist nullptr; //指向待释放的空间size_t _remainbytes 0; // 大块内存在切分过程中剩余字节数 };成员变量 _memory这是一个char*类型的指针初始化为nullptr。它的作用是指向大块内存。选择char*类型是为了方便进行地址的加减运算因为char类型在内存中占用一个字节。这个大块内存就是我们内存池的核心资源用来存储对象。_freelist类型为void*初始值是nullptr。它就像是一个管理空闲内存块的列表当对象被释放后相关的内存块会被添加到这个列表中以便后续的复用。_remainbytes这是一个size_t类型的变量初始化为0。它记录了大块内存在切分过程中剩余的字节数。这个变量对于判断何时需要重新申请大块内存非常关键。 二内存分配 - New函数 T* ObjectPool::New() {T* obj nullptr;if (_freelist) //_freelist非空说明有还回来的内存优先使用还回来的内存//可以使用类似无头结点链表的头删{void* next *(void**)_freelist;obj (T*)_freelist;_freelist next;}else{if (_remainbytes sizeof(T)) {_remainbytes 128 * 1024;/*如果空间无法被T整除可能会出现会有一块内存不够一个T对象的空间所以会导致_remainBytes的结果非零而空间又不够用了因此if中的判定应该是_remainBytes sizeof(T),即剩余内存不够一个对象大小时则重开大块空间以避免越界访问*/_memory (char*)malloc(128 * 1024);if (_memory nullptr) {throw std::bad_alloc();}}obj (T*)_memory; //申请出去的新的空间的起始位置//_memory sizeof(T); //新的空间被申请后剩余空间的起始位置size_t objsize sizeof(T) sizeof(void*) ? sizeof(void*) : sizeof(T);_memory objsize;/*由于指针变量的大小是4个字节32位或者8个字节64位而回收内存的时候是通过链表回收的需要存储加一个节点的地址因此如果当T类型的类型大小小于指针大小的时候无法存储一个地址例如32位下指针大小4个字节而char类型1个字节这时候回收链表中只有一个字节的大小的话显然无法存放一个地址因此需要作比较如果类型大小小于指针变量类型大小则返回一个指针变量的大小。*/_remainbytes - sizeof(T); //剩余的空间}//定位new显式调用T的构造函数初始化new(obj)T;return obj;}优先使用空闲内存 当调用New函数来获取一个对象时首先会检查_freelist是否为空if (_freelist)。如果_freelist不为空说明有已经回收但还未重新分配的内存。这时候就会采用类似无头结点链表的头删操作来获取内存块。具体来说先获取_freelist指向的内存块通过void* next *(void**)_freelist;然后将这个内存块作为新对象的地址obj (T*)_freelist;最后更新_freelist指向该内存块中的下一个空闲内存的指针_freelist next;。 从大块内存分配 如果_freelist为空就需要从大块内存_memory中分配空间。在分配之前会检查_remainbytes是否小于sizeof(T)。如果是就意味着当前大块内存剩余空间不足以分配一个T类型的对象了。这时候会重新申请一块128 * 1024字节的内存_remainbytes 128 * 1024; _memory (char*)malloc(128 * 1024);并且在申请失败时抛出std::bad_alloc异常。在分配内存时还有一个很巧妙的地方。由于在回收内存时是通过链表来管理的需要存储下一个节点的地址而不同机器上指针类型的大小可能不同32位机器上为4字节64位机器上为8字节。所以在计算分配的字节数时会比较sizeof(T)和sizeof(void*)的大小size_t objsize sizeof(T) sizeof(void*)? sizeof(void*) : sizeof(T);取较大值来确保能够正确存储下一个空闲对象的指针。然后将新对象的地址赋给obj并更新_memory和_remainbytesobj (T*)_memory; _memory objsize; _remainbytes - sizeof(T);。最后通过定位newnew(obj)T;显式调用T的构造函数来初始化这个新对象。 三内存回收 - Delete函数 void ObjectPool::Delete(T* obj) {//显式调用析构函数清理对象obj-~T();//就相当于无头结点链表的头插*(void**)obj _freelist;_freelist *(void**)obj;/*之所以使用void类型来强制转换只因为在不同位机器下地址的字节位数不同比如在32位下指针类型的大小是四个字节而在64位下指针类型的大小是八个字节因此如果想要通过二级指针来修改obj的指向同时兼顾不同机器使用void类型来进行强制类型转换。*/}清理对象资源 当调用Delete函数来释放对象时首先会显式调用对象的析构函数obj-~T();这一步确保对象内部的资源被正确清理。 归还内存到空闲列表 然后将释放的内存块添加到_freelist的头部这一操作类似于无头结点链表的头插操作。通过*(void**)obj _freelist; _freelist *(void**)obj;来实现。这里使用void*类型进行强制转换是为了在不同位机器下地址字节位数不同能够正确地通过二级指针修改obj的指向从而将对象的内存块添加到空闲内存列表中。 三、内存池在TreeNode示例中的性能测试演示 struct TreeNode//二叉树节点 {int _val;TreeNode* _left;TreeNode* _right;TreeNode():_val(0), _left(nullptr), _right(nullptr){} };void TestObjectPool() {// 申请释放的轮次const size_t Rounds 5;// 每轮申请释放多少次const size_t N 100000;std::vectorTreeNode* v1;v1.reserve(N);size_t begin1 clock();for (size_t j 0; j Rounds; j){for (int i 0; i N; i){v1.push_back(new TreeNode);}for (int i 0; i N; i){delete v1[i];}v1.clear();}size_t end1 clock();std::vectorTreeNode* v2;v2.reserve(N);ObjectPoolTreeNode TNPool;size_t begin2 clock();for (size_t j 0; j Rounds; j){for (int i 0; i N; i){v2.push_back(TNPool.New());}for (int i 0; i N; i){TNPool.Delete(v2[i]);}v2.clear();}size_t end2 clock();cout new cost time: end1 - begin1 endl;cout object pool cost time: end2 - begin2 endl; } int main() {TestObjectPool();return 0; }debug下运行效果 release下运行效果 可以看到使用对象池的程序运行效率会高不少 普通的new/delete操作 在TestObjectPool函数中首先对普通的new和delete操作进行了测试。创建了一个std::vectorTreeNode*类型的v1并预留了N个元素的空间v1.reserve(N);。然后通过两层循环外层循环Rounds次内层循环N次。在每次内层循环中使用new创建一个TreeNode对象并添加到v1中v1.push_back(new TreeNode);然后再使用delete释放这些对象delete v1[i];最后清空v1向量v1.clear();。通过记录这个过程开始的时间size_t begin1 clock();和结束的时间size_t end1 clock();就可以计算出使用普通new/delete操作的耗时。 使用内存池的操作 接着创建了一个ObjectPoolTreeNode类型的对象池TNPool同样创建了一个std::vectorTreeNode*类型的v2并预留N个元素的空间。也是通过类似的两层循环在每次内层循环中使用对象池的New函数获取TreeNode对象并添加到v2中v2.push_back(TNPool.New());然后使用对象池的Delete函数释放对象TNPool.Delete(v2[i]);最后清空v2向量。同样记录这个过程开始的时间size_t begin2 clock();和结束的时间size_t end2 clock();从而得到使用内存池操作的耗时。最后通过cout输出两种方式的耗时cout new cost time: end1 - begin1 endl; cout object pool cost time: end2 - begin2 endl;。从这个结果可以直观地看到在频繁创建和销毁TreeNode对象的场景下使用内存池能够带来明显的性能提升。 四、脱离malloc直接在堆上按页申请空间 #ifdef _WIN32 #includewindows.h #else // #endif// 直接去堆上按页申请空间 inline static void* SystemAlloc(size_t kpage) { #ifdef _WIN32void* ptr VirtualAlloc(0, kpage 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); #else// linux下brk mmap等 #endifif (ptr nullptr)throw std::bad_alloc();return ptr; }预处理 #ifdef指令 #ifdef是C和C中的预处理指令。它用于条件编译在这里的含义是“如果定义了ifdef是if defined的缩写符号_WIN32”。在不同的编译环境下可能会预定义一些特定的符号。在Windows环境下编译时通常会预定义_WIN32这个符号在64位Windows下可能还会预定义_WIN64但这里只关注_WIN32相关的逻辑。 包含头文件windows.h 当_WIN32被定义时即代码在Windows环境下编译#include windows.h会被执行。windows.h是一个非常重要的Windows平台的头文件它包含了大量Windows系统相关的函数声明、数据类型定义、宏定义等内容。例如Windows下的图形界面编程使用Windows API、系统调用、进程和线程相关的操作等很多功能都需要这个头文件中的定义来支持。 #else和#endif #else是与#ifdef配合使用的预处理指令表示“否则”的情况。在这里如果_WIN32没有被定义即代码不是在Windows环境下编译那么会执行#else后面的内容。不过在这段代码中#else后面只是一个注释//没有实际的代码可能是预留的用于在非Windows环境下如Linux、macOS等添加相关代码的地方。#endif是#ifdef - #else结构的结束标志表示条件编译块的结束。如果没有#endif编译器会报错因为它不知道条件编译块在哪里结束。 SystemAlloc函数 函数声明部分 inline static void* SystemAlloc(size_t kpage) inline关键字这是一个C 中的关键字用于建议编译器将函数内联化。内联函数的主要目的是减少函数调用的开销。当函数被标记为内联时编译器在编译时会尝试将函数体直接嵌入到调用该函数的地方而不是像普通函数调用那样进行压栈、跳转等操作。static关键字在这里表示函数具有内部链接性即这个函数只能在当前的源文件中被访问不能被其他源文件访问。void*表示函数的返回类型是一个通用指针指向未知类型数据的指针。SystemAlloc函数名。size_t kpage函数接受一个size_t类型无符号整数类型通常用于表示对象的大小或数组的长度等的参数kpage。 #ifdef _WIN32条件编译部分Windows平台下的实现 void* ptr VirtualAlloc(0, kpage 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); VirtualAlloc是Windows系统中的一个函数用于在进程的虚拟地址空间中分配内存。 第一个参数0表示让系统自动选择分配内存的起始地址。第二个参数kpage 13这里使用了左移运算符。kpage是传入的参数表示某种数量可能是页数之类的概念左移13位相当于将kpage乘以2^13即8192。这意味着以页为单位计算要分配的内存大小在Windows中内存页大小通常是4KB或8KB这里假设是8KB那么kpage就表示要分配的页数。第三个参数MEM_COMMIT | MEM_RESERVE这是内存分配类型的标志组合。MEM_COMMIT表示提交内存即将物理存储映射到进程的虚拟地址空间MEM_RESERVE表示保留一块地址空间这块空间可以被后续的操作如提交内存使用。第四个参数PAGE_READWRITE表示分配的内存具有可读可写的保护属性。 #else部分非Windows平台下的占位代码 在非Windows平台下即_WIN32未被定义时这里只是一个注释// linux下brk mmap等说明在Linux平台下可能会使用brk或mmap等系统调用或函数来实现类似的内存分配功能但目前没有实际的代码实现。 内存分配失败处理部分 if (ptr nullptr) 这个if语句用于检查内存分配是否成功。如果ptr为nullptr即VirtualAlloc函数在Windows平台下分配内存失败或者在非Windows平台下如果有实现且分配失败时也应该遵循类似的逻辑则表示内存分配失败。 throw std::bad_alloc(); 当内存分配失败时函数会抛出std::bad_alloc异常。std::bad_alloc是C 标准库中定义的用于表示内存分配失败的异常类型。当使用new操作符分配内存失败时也会抛出这个异常。 函数返回部分 return ptr; 如果内存分配成功在Windows平台下VirtualAlloc成功或者在非Windows平台下假设的brk、mmap等操作成功函数将返回分配得到的内存地址指针。 使用这种方式修改后的代码如下 #ifdef _WIN32 #includewindows.h #else // #endif// 直接去堆上按页申请空间 inline static void* SystemAlloc(size_t kpage) { #ifdef _WIN32void* ptr VirtualAlloc(0, kpage 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); #else// linux下brk mmap等 #endifif (ptr nullptr)throw std::bad_alloc();return ptr; }templateclass T class ObjectPool { public:T* New() {T* obj nullptr;if (_freelist) {void* next *(void**)_freelist;obj (T*)_freelist;_freelist next;}else{if (_remainbytes sizeof(T)) {_remainbytes 128 * 1024;//_memory (char*)malloc(128 * 1024);_memory (char*)SystemAlloc(_remainbytes 13);if (_memory nullptr) {throw std::bad_alloc();}}obj (T*)_memory; //申请出去的新的空间的起始位置//_memory sizeof(T); //新的空间被申请后剩余空间的起始位置size_t objsize sizeof(T) sizeof(void*) ? sizeof(void*) : sizeof(T);_memory objsize;_remainbytes - sizeof(T); //剩余的空间}//定位new显式调用T的构造函数初始化new(obj)T;return obj;}void Delete(T* obj) {obj-~T();*(void**)obj _freelist;_freelist *(void**)obj;} private:char* _memory nullptr; //指向大块内存的指针void* _freelist nullptr; //指向待释放的空间size_t _remainbytes 0; // 大块内存在切分过程中剩余字节数 };五、总结 一内存池的优势 性能提升 通过避免频繁的系统级内存分配malloc和释放free操作内存池减少了内存管理的开销。在处理大量对象的创建和销毁时这种优势更加明显。就像我们在TestObjectPool函数中的测试结果一样使用内存池操作TreeNode对象比普通的new/delete操作要快很多。 内存碎片减少 内存池通过预先分配大块内存并对其进行有效的管理和复用减少了内存碎片的产生。这有助于提高内存的利用率使程序能够更稳定地运行尤其是在长时间运行的程序或者内存资源紧张的环境中。 二代码的思考 可扩展性 由于ObjectPool是一个模板类它可以方便地应用于各种类型的对象只要这些对象的构造函数和析构函数定义正确。这为代码的复用和扩展提供了很大的便利使我们可以在不同的项目中轻松地使用这个内存池来管理不同类型的对象。 局限性 内存池的实现也存在一些局限性。例如它是基于定长内存池的思想每次重新申请内存时固定为128 * 1024字节。在处理大小差异很大的对象时可能会导致内存浪费或者频繁重新申请内存的情况。另外这个代码没有考虑多线程环境下的并发安全问题如果在多线程中同时使用这个内存池可能会导致数据竞争和错误的内存管理操作。
http://www.dnsts.com.cn/news/49151.html

相关文章:

  • 深圳傻瓜式网站建设公司好吗咸阳网站建设公司哪家好
  • 礼品册兑换 网站建设建搜索型网站
  • 请人做竞价网站的要求重点ftp网站建设
  • 南京快速建站模板下载网站建设方案模板
  • 怎么创建网站自己创建汕头网站设计怎么做
  • 灵璧哪有做网站的涂料网站模板
  • 淘宝客网站一定要备案吗wordpress系统通知邮箱
  • 怎么建设电子商城网站东莞市建设工程信息服务协会
  • 系部网站建设标准学建网站
  • 成功的营销网站自动写作网站
  • 无锡做网站公司多少钱品牌策划公司有哪些
  • 汉口网站推广公司如何网页制作
  • 编程代码网站wordpress菜单左对齐
  • 如何制作一个自己的网页网站宗亲网站建设建议
  • 电子工程网站有哪些百度seo排名曝光行者seo
  • 郴州网站建设软件定制开发制作企业目录
  • 网站建设必须要具备哪些知识网络设计报告提纲范文
  • 一站式服务大厅官网建设摩托车官网首页
  • 淘客怎么建网站做推广百度怎么做开锁网站
  • 电子商务网站建设主题可以做网站的软件上传歌曲
  • 做带v头像的网站国外优秀企业网站欣赏
  • 学校内部网站开发价格在哪个网站上做苗木生意好些
  • 外贸免费开发网站建设长沙网站建设流程
  • wordpress站点如何添加百度分享代码建设银行网站怎么登陆不了了
  • 网站后台修改网站首页怎么做做网站背景图片浪漫爱情
  • 南京城市规划建设展览馆网站重庆企业网站推广价格
  • 网站后台管理系统权限做免费嗳暧视频网站
  • 华强北附近网站建设WordPress 4.8.1 增强版
  • 功能型网站wordpress 换语言
  • 网站app客户端制作林州市住房和城乡建设部网站