C++学习10-STL内存池

参考博文链接在这里

STL介绍

STL 是 C++ 标准程序库的核心。STL 内的所有组件都由模板构成,其元素可以是任意型别。程序员通过选用恰当的群集类别调用其成员函数和算法中的数据即可,但代价是 STL 晦涩难懂。

C++ 对模板(Template)支持得很好,STL 就是借助模板把常用的数据结构及其算法都实现了一遍,并且做到了数据结构和算法的分离。
例如,vector 的底层为顺序表(数组),list 的底层为双向链表,deque 的底层为循环队列,set 的底层为红黑树,hash_set 的底层为哈希表。

STL 组件主要包括容器,迭代器、算法和仿函数。
容器
容器即用来存储并管理某类对象的集合。例如鱼缸是用来盛放金鱼的容器。

每一种容器都有其优点和缺点。为满足程序的各种需求,STL 准备了多种容器类型,容器可以是 arrays 或是 linked lists,或者每个元素有特别的键值。

迭代器
迭代器用于在一个对象群集的元素上进行遍历动作。对象群集可能是容器,也可能是容器的一部分。

迭代器的主要用途是为容器提供一组很小的公共接口。利用这个接口,某项操作可以行进至群集内的下一个元素。

每种容器都提供了各自的迭代器。迭代器了解该容器的内部结构,所以能够正确行进。迭代器的接口和一般指针类似。

算法
算法用来处理群集内的元素,可以出于不同目的搜寻、排序、修改、使用那些元素。所有容器的迭代器都提供一致的接口,通过迭代器的协助,算法程序可以用于任意容器。

扫描二维码关注公众号,回复: 10938470 查看本文章

SIL 中提供的算法包括搜寻、排序、复制、重新排序、修改、数值运算等。

仿函数
STL中大量运用了仿函数。仿函数具有泛型编程强大的威力,是纯粹抽象概念的例证

STL 特性:
STL 的一个特性是将数据和操作分离。数据由容器类别加以管理,操作则由可定制的算法定义。迭代器在两者之间充当“粘合剂”,以使算法可以和容器交互运作。

STL 的另一个特性即组件可以针对任意型别运作。“标准模板库”这一名称即表示“可接受任意型别”的模板,并且这些型别均可执行必要操作。

在 STL 中,容器又分为序列式容器和关联式容器两大类,而迭代器的功能主要是遍历容器内全部或部分元素的对象。迭代器可划分为 5 种类属,这 5 种类属归属两种类型:双向迭代器和随机存取迭代器。

内存分配

sgi stl的二级空间配置器,把<=128 字节的内存分配,由内存池进行管理,把>128 字节的内存,通过一级空间配置器malloc和free进行管理 `
在这里插入图片描述第一级配置器:
第一级采用malloc、free;
这个配置器提供了当内存配置错误时的处理函数oommalloc,这个函数会调用_malloc_alloc_oom_handler()这个错误处理函数,去企图释放内存,然后重新调用malloc分配内存。如此循环,直到分配成功,返回指针(所以再一定程度上提高内存分配成功)。
第二级配置器
二级空间配置器对内存的管理减少了小区块造成的内存碎片,它主要是:如果所要申请的空间大于128字节,则直接交至一级空间配置器处理,如果小于128字节,则使用二级空间配置器,它是用一个16个元素的自由链表来管理的,每个位置下挂载着大小(分别为8、16、24、32、48、56、64、72、80、88、96、104、112、120、128字节),每次将所需内存提升到8的倍数。
free-list节点结构是一个共用体

union obj
{
     union obj * free_list_link;     //下一个节点的指针
     char client_data[1];                    //内存首地址
}

二级空间配置器结构如下图所示:
在这里插入图片描述自由链表中分出区块:
这样在完成工作的同时更加节省空间。在空间没有分配出去之前,该空间中一直存放的是所指向的下一个节点的地址,当该空间分配出去时,让对应的my_free_list指向它的下一个节点,然后将他内部的值改为要放入用户的data值
在这里插入图片描述在这里插入图片描述释放内存
在这里插入图片描述

内存池的思路

  1. 使用allocate向内存池请求size大小的内存空间, 如果需要请求的内存大小大于128bytes, 直接使用malloc.
  2. 如果需要的内存大小小于128bytes, allocate根据size找到最适合的自由链表.
      a. 如果链表不为空, 返回第一个node, 链表头改为第二个node.
      b. 如果链表为空, 使用blockAlloc请求分配node.
        x. 如果内存池中有大于一个node的空间, 分配竟可能多的node(但是最多20个), 将一个node返回, 其他的node添加到链表中.
        y. 如果内存池只有一个node的空间, 直接返回给用户.
        z. 若果如果连一个node都没有, 再次向操作系统请求分配内存.
          ①分配成功, 再次进行b过程
          ②分配失败, 循环各个自由链表, 寻找空间
            I. 找到空间, 再次进行过程b
            II. 找不到空间, 抛出异常(代码中并未给出, 只是给出了注释)
  3. 用户调用deallocate释放内存空间, 如果要求释放的内存空间大于128bytes, 直接调用free.
  4. 否则按照其大小找到合适的自由链表, 并将其插入.

内存池特点

  1. 刚开始初始化内存池的时候, 其实内存池中并没有内存, 同时所有的自由链表都为空链表.
  2. 只有用户第一次向内存池请求内存时, 内存池会依次执行上述过程的 1->2->b->z来完成内存池以及链表的首次填充, 而此时, 其他未使用链表仍然是空的.
  3. 所有已经分配的内存在内存池中没有任何记录, 释放与否完全靠程序员自觉.
  4. 释放内存时, 如果大于128bytes, 则直接free, 否则加入相应的自由链表中而不是直接返还给操作系统.

为什么要有空间配置器

1、小块内存带来的内存碎片问题 单从分配的角度来看。由于频繁分配、释放小块内存容易在堆中造成外碎片(极端情况 下就是堆中空闲的内存总量满足一个请求,但是这些空闲的块都不连续,导致任何一个单独 的空闲的块都无法满足这个请求)。
2、小块内存频繁申请释放带来的性能问题。 关于性能这个问题要是再深究起来还是比较复杂的,下面我来简单的说明一下。 开辟空间的时候,分配器会去找一块空闲块给用户,找空闲块也是需要时间的,尤其是 在外碎片比较多的情况下。如果分配器其找不到,就要考虑处理假碎片现象(释放的小块空 间没有合并),这时候就要将这些已经释放的的空闲块进行合并,这也是需要时间的。 malloc 在开辟空间的时候,这些空间会带有一些附加的信息,这样的话也就造成了空间 的利用率有所降低,尤其是在频繁申请小块内存的时候。

一些知识点

开辟内存为什么要考虑内存对齐

是因为内存取整可以降低CPU读取内存的次数,提高性能。主要为减少内存的I/O操作,由于此操作性能太低。

为什么要进行内存对齐:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问(I/O 读写操作),而对齐的内存访问仅需要一次访问。
1.为了更高效的读数据
2.为了更好地管理

nginx内存池的大块内存和小块内存的界

是max即一页内容=4096字节=4k

小块内存为什么不提供释放函数

因为http协议本身就是一个短连接,无状态的协议。

重置函数ngx_reset_pool什么时候调用

处理完请求,服务器断开。在处理完http请求完成之后,就可以发起内存池的重置函数

惊群现象

创建4个进程 accept(8000) 每个进程都在监听8000端口,当用户连接8000端口,4个进程都被唤醒,但是只有一个能拿到socket和用户连接,其他进程被唤醒了一圈阻塞了又回去了.这样就不好了.

epoll 8000已经解决了惊群现象了.
在nginx中,多线程实现的网络程序,每个进程都使用了epoll,解决了服务器惊群现象.所以nginx很好用

nginx适合短链接

nginx用的是 http服务器,是短连接的.所以ngnix内存池适合间歇性提供服务的服务器应用场景,短链接应用场景.
http服务器接收浏览器的请求,使用http协议,是应用层协议,

长连接:聊天
短连接:网页.  如果不停的不间断的访问,就会无法重置,会崩.

nginx
反向代理代理的是:服务器
正向代理代理的是:客户端

正向代理:(向自己不熟悉的校长借钱)
我要访问谷歌,先把请求给代理服务器上发送,再发到谷歌上面去.
反向代理:(向一个没有钱但朋友多的人借钱)
客户端访问服务器,有多个服务器,不可能让客户端自己选择服务器,应该是连接某一个设备看哪个服务器CPU负载更少,就分给他,这就叫做负载均衡反向代理服务器.
发布了82 篇原创文章 · 获赞 7 · 访问量 4188

猜你喜欢

转载自blog.csdn.net/sunshine612/article/details/105007938
今日推荐