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

面试问题之高并发内存池项目

项目部分
1.这个项目是什么?
高并发内存池的原型是谷歌一个开源项目,tcmalloc,而这个项目,就是tcmalloc中最核心的框架和部分拿出来进行模拟。他的作用就是在去代替原型的内存分配函数malloc和free。这个项目涉及的技术有,c++,数据结构(双向链表,哈希桶),操作系统内存管理,多线程,互斥锁,单例模式等方面的技术。

2.这个项目是怎么组成的?
由threadcache,centralcache,和pagecache三部分组成,什么意思呢,当用户使用我们这个项目去申请内存时,进入这个函数后,先去threadcache申请内存,如果不够,再去向第二层centracahhe去申请内存,要是还是不行,就去第三层申请内存,最后把申请到的内存返回给用户。这就是,tcmalloc的大概实现,当然了,很多细节还没展开说。

3.这个项目实现后,效果怎么样?
在项目完成后,拿我们实现的tcmalloc去和原先的malloc对比。
申请固定内存大小时,我创建了4个线程,分别执行10轮操作,每轮n申请释放1万次,也就是一共进行了40万次的申请释放,结果是malloc用了31ms左右,而tcmlloc只用了11ms左右,就是快了3倍。
当我尝试申请不同内存时,同样的,4个线程,执行10轮,1轮1万次,共40万次申请释放操作,这次,malloc的时间用了1023ms,而tcmalloc只用了113ms,倍数来到了10倍,也就是说,理想状况下,在未来的多线程开发环境下申请不同的内存,使用malloc函数如果要用10s才能完成内存的申请和释放,我只要用1s。

4.你在这个项目收获了什么?
调试方面:
一,加深了条件断点理解和使用,还可以设置具体断点条件让它停下来
二:学会了使用查看函数栈帧,当在断点处停下,当前函数没问题,就可以调用函数堆栈去看看调用这个函数的函数有没有问题。(蛮实用的)
三:疑似死循环的处理
当我们设置了断点,但它迟迟没有到达断点处,程序也没有崩溃,就可能进入死循环了,这时候全部中断,程序就会停在死循环的部分。

对操作系统内存管理的理解:
以前认为申请内存就是简单从内存里哇一块过来就行,实现了这个项目后,才发现,申请内存的时候可能有多层内存池,这个没有空间,再逐步往后申请,再比如,申请不同内存块,再造成的内存碎片问题啊等等

对c++语法和数据结构更加熟悉,项目是c++,虽然代码不是很多,但都是精华,涉及了很多语法的使用,比如智能指针,强制转换,类的使用啊,还有数据结构啊,对链表的操作,插入删除,哈希桶的操作,对齐映射等等,加强了我对代码的掌控力

5.你在这个项目遇到的最大困难是什么?
可能就是调试的时候吧,因为我是写完一个功能然后调试一下,确保每个功能没有问题,然后到了最后,我感觉没啥大问题了,然后十多个文件,一运行,得得得得,一排排错误,把我整懵了,就开始调试了,除了一些语法,其中比较难调试的,是申请内存的一套龙过程,经常调试过程中要么就是因为空指针,要么就是走到不该走的地方去了,后面加了应有的条件判断,还是函数功能的完善,问题就越来越少,当时看蛮兴奋的。
还有一个比较大的问题,当时比较tcmalloc和malloc的效率,发现tcmalloc还比malloc申请释放内存要慢一点,然后我就上网查为什么,怎么解决,后面发现可以用vs自带的性能分析,设置成debug模式,然后进入调试的性能诊断,后面发现自己的有个函数占了时间的近一半(mapobjecttospan函数–地址和页号的映射关系),然后发现这个函数加了锁,恰恰这个函数又经常被调用,就造成了效率低下,然后我就查资料,csnd,说用基数树不用加锁,又能完成原先的函数功能,我就把基数树套上去,后就发现果然快了很多

6.为什么基数树不要加锁?
因为不可能存在多个线程对同一个页进行读取映射和建立映射的操作,读取映射的时候是在span中use_count不为0时,而建立映射是在usecount为0的时候,一个页中的usecount没有两种状态,所以不可能出现多个线程对一个页进行读取映射和建立映射的操作。

7.那一个线程读取映射,一个线程建立映射,怎么办呢?
如果是红黑素哈希表的话那就会有影响,哈希和红黑树的插入,删除都会影响到整个数的一个情况。,所以必须要加锁,但如果是基数树的话,因为它的空间一旦开好,就是固定的,所以不用担心建立其他页的映射会影响本页的读取,就和一个固定大小的数组一样,不需要加锁

8.那说说你在这个项目对锁的使用吧?
这个项目三部分可能会涉及线程安全,第一部threadcache,我的解决方案是,使用线程局部储存,使得每一个线程都有自己的专属内存,在这个线程内部是全局可见的,但在其他线程那是不可访问的,所以通过这种方法就不必再加锁了,因为他本身就解决了线程安全问题了
第二部分有线程安全问题的cencatalacache,当多个线程向哈希桶拿内存的时候会有问题,所以我给它加了个哈希桶锁,不用把整个哈希桶都锁起来,这样一来,只有线程访问相同一个桶的时候,才有加锁解锁的消耗,这也是他效率高的原因一部分
第三步有线程安全的是,当多个线程访问pagecache的哈希桶时会有线程安全,因为在这里一个线程操作会影响多个地方的桶,加锁解锁太频繁了,所以在这里,我选择直接加把大锁,一次只能一个线程访问pagecache的哈希桶。

9.那说说用户在申请一块内存要经过哪些流程吧?小伙子!
好的,面试官!
假设用户要申请一个7字节的内存,然后调用tcmalloc这个函数,进入这个函数后,我们先把7字节对齐成8,这样做是为了避免内存碎片,对齐的字节方便后续我们对内存碎片的处理。对齐后,线程第一次申请内存,通过线程局部存储获得自己专属的threadcache,然后通过threadcache去申请内存,对齐的字节数是8,那我们就找到threadcache的哈希桶的0下标去获取一个8字节的内存块,然后发现没有,这时候就找二哥,centralcache,因为哈希桶的对齐映射关系是一致的,线程也去centalcache的哈希桶0号去获取一个非空span,接着计算thread要多少个8字节内存块,这一块我使用的是慢反馈算法,第一次给一个,第二次给两个,第三次给三个,越就是你调用的越多,以后我就返回越多个内存块给你,但最多返回512个,这次是第一次,就返回一个8字节内存块就行,那就哈希桶里找啊,找之前加锁,发现一个非空的span都没有,就去找大哥,找之前解锁,然后加上大哥的锁,找大哥要多少呢,三弟刚刚要一个然后乘以他要的字节,也就是8个字节,不满一页按一页申请,大哥就去自己的第一个哈希桶找有没有1页的内存卡,发现没有,就往后找,发现还是没有,就向系统申请一个128页的内存块,然后继续找,把128拆成一个一页的和一个127页的,返回一页给二弟,二弟把这个一页切成512的8字节挂在自己的0号哈希桶,返回一个给三弟用。至此,整个申请内存流程大致完成。

10.说的不错,那你说说你释放的流程吧?
厚礼谢,感谢!
这个项目的释放内存函数叫tcfree。
当用户通过tcfree释放内存块时,线程会把这个内存卡先挂在三弟的对应的哈希桶后面,如果这个哈希桶后的链表数量超过了我们设定的值,就把这段链表,取出来,挂着二哥对应的哈希桶后面,同时查找映射表看看,不同的内存块挂着不同的span后面,确保每个span后跟着的内存块都是span这个范围里的。同样的,当二哥后面挂的链表过长,满足一页大小之后,就把他取下来,然后挂着大哥对应的哈希桶后面,同时,从这页开始,尝试向前合并页数,向后合并页数,这里返回的是一页就尝试向后看看有没有2页的,有就合成三页,然后把1/2页的span删除,继续向后合并,直至遇到空桶。至此,这个项目大概释放内存流程就走完了

11.给定100亿个整数,设计算法找到只出现一次的整数
在这里插入图片描述

C++语法部分

1.1.谈谈智能指针吧?
智能指针就是利用对象的生命周期来控制程序资源,当对象出了作用域,就自动调用对象的析构函数,就能解决内存泄露问题了

2.智能指针有哪几种呢?讲讲他们的区别?
根据解决智能指针拷贝问题方法的不同,把智能指针大概分以下几种
第一种autoptr,他是通过转移资源所有权来解决拷贝问题,也就是假如有一个智能指针a指向一块资源,这时候用a拷贝出一个b智能指针,此时,b就接管了资源的所有权,a就废了。
第二种,是uniqueptr,他的解决方法是通过把类的拷贝构造函数和拷贝赋值函数私有化,直接不让进行智能指针的拷贝
第三种,sharedptr,他是通过引用计数来解决智能拷贝问题,每一块资源都对应着一个引用计数,当增加了一个智能指针指向这块资源,引用计数就加加,如果有指针不指向这块资源了,智能指针不必直接调用析构函数去释放这块内存,而是将引用计数减一,如果此时为0,则进行调用析构。
ps,这个引用计数变量是在堆区,这块资源的所有智能指针对象共享这一个变量,不能放栈区,那每个个智能指针都有一个计数了,也不能变为全局或者变为静态变量,那么就是所有资源的所有对象都共享这个计数,也不对。
第四种,是为了解决,sharedptr的循环引用的问题(当智能指针管理的是节点是,并且节点一指向节点二,节点二指向节点一,那么此时,双方的引用计数都是2,当节点一,节点二出了作用域,引用计数减为一,但此时会进入死胡同,节点一的资源释放取决于,节点二中的prev变量,节点二资源的释放取决于节点一中的next变量的释放,而节点一中的next的释放又取决于节点一,节点二中的prev释放取决于节点二的资源释放,就进入死循环了)是weakptr,他把节点中的prev和next指针换成了weakptr指针,构造出来的weakptr指针和sharedptr一块管理资源,但不会增加引用计数,就解决其的循环引用问题了

3.c有强制转换为什么还要,增加其他的类型转换。
一,把所以情况混在一起,可读性低
二,有时会减少精度

4.c++有哪些类型转换?
staticcast用于两个类型相近的转换,比如int和double的转换
reinterpretcast用于两个类型不同的转换
constcast用于去除const属性,使用后就可以对const变量进行修改了
PS,const变量认为其不会变,就变值放在寄存器中,但我们修改的是内存中的值,所以打印出来还是原来的值,因为此时OS是从寄存器中拿的值。这是我们可以用volatie关键词对const进行修饰,这样就不会做多余的优化,而且去内存中拿值了
最后一种类型转换是dynamic,是一种向下转换,将父类的指针或引用转化成子类的指针或引用,但如果父类的指针原先是指向父类的,转换成子类的指针,它可能会调用子类的资源,那么就是不安全的,此时调用dynamiccast就会失败并返回一个空指针

5.谈谈进程通信中的管道传输吧?
管道通信是父子进程的一种通信方式,父进程先创建管道,然后再创造子进程,然后父子进程关掉相应的读写文件符,确定谁向谁输入。管道通信自带互斥和同步,
要不就是父进程在操作管道,要么就是子进程在操作,当写满了,写端进程就被挂起,待管道有空间了,再唤醒写端。管道通信是半双工通信,要实现双方同时通信,需要两个管道,此为匿名管道,pipe函数创建匿名管道,参数是一个输出型数组,返回两个指向管道的读写文件描述符。

6.再谈谈命名管道吧?
两个无亲缘关系的进程,可通过命名管道的文件名打开同一份管道文件进行通信,mkpipo函数,第一个参数是表示要创建命名管道名字,是以路径还是文件名(默认当前路径创建),第二个参数是以什么权限打开,创建一个命名管道,服务端以读的方式,客户端以写的方式打开,就可以通信了。

7.二者区别?
匿名管道和命名管道的区别在于,创建方式(函数)和打开方式(匿名默认都打开,命名要自己去选择)

8.共享内存通信?
在物理空间申请一块共享空间,然后将不同进程的页表和这块空间建立映射,同时在其各自的虚拟空间中开辟空间,把虚拟空间也和其页表建立映射,那么虚拟空间就和物理空间映射起来了,这些进程就看见了同一份内存,这一份内存就叫共享内存。创建共享内存用得是shmget函数第一个参数是key,标识这块共享内存在系统的唯一性,第二个参数是这块内存的大小,第三个参数是创建共享内存的方式。

9.共享内存和管道比较的优缺点?
优点,只需要拷贝两次,而管道要拷贝四次,所以最快。
缺点,管道自带互斥和同步,而共享内存啥也没有。

10.谈谈消息队列吧?
消息队列让不同进程看到同一份资源的方法是,创建一个队列,队列里都是数据块,进程a和进程b要写入信息都在队列的尾部,读取都在队列的头读取,创建函数是msgget函数,第一个参数是key值,是系统中的唯一标识,第二个参数是打开方式。

全靠龙哥博客活着,
各位看官,要是想搞明白八股文去看2021dragon(龙是这样打的吧),超经典!

相关文章:

面试问题之高并发内存池项目

项目部分 1.这个项目是什么? 高并发内存池的原型是谷歌一个开源项目,tcmalloc,而这个项目,就是tcmalloc中最核心的框架和部分拿出来进行模拟。他的作用就是在去代替原型的内存分配函数malloc和free。这个项目涉及的技术有,c&…...

如果阿里巴巴给蒋凡“百亿补贴”

出品 | 何玺 排版 | 叶媛 2021底,阿里内部进行组织架构大调整,任命蒋凡为阿里海外商业负责人,分管全球速卖通和国际贸易(ICBU)两个海外业务,以及Lazada等面向海外市场的多家子公司。 一年时间过去&#x…...

Linux版本现状

Linux的发行版本可以大体分为两类,一类是商业公司维护的发行版本,一类是社区组织维护的发行版本,前者以著名的Red Hat(RHEL红帽)为代表,后者以Debian为代表。Red HatRedhat,应该称为Redhat系列&…...

Winform中实现保存配置到文件/项目启动时从文件中读取配置(序列化与反序列化对象)

场景 Winform中实现序列化指定类型的对象到指定的Xml文件和从指定的Xml文件中反序列化指定类型的对象: Winform中实现序列化指定类型的对象到指定的Xml文件和从指定的Xml文件中反序列化指定类型的对象_winform xml序列化_霸道流氓气质的博客-CSDN博客 上面讲的序…...

基于python的超市历年数据可视化分析

人生苦短 我用python Python其他实用资料:点击此处跳转文末名片获取 数据可视化分析目录人生苦短 我用python一、数据描述1、数据概览二、数据预处理0、导入包和数据1、列名重命名2、提取数据中时间,方便后续分析绘图三、数据可视化1、美国各个地区销售额的分布&…...

GPT-4技术报告

摘要 链接:https://cdn.openai.com/papers/gpt-4.pdf 我们汇报了GPT-4的发展,这是一个大规模的多模态模型,可以接受图像和文本输入并产生文本输出。虽然在许多现实场景中,GPT-4的能力不如人类,但它在各种专业和学术基…...

前端性能优化

总结 使用打包工具对代码进行打包压缩;引入css时采用link标签,并放入头部,使其与文档一起加载,减少页面卡顿时间;尽量减少dom结构的重排和重绘;使用css雪碧图,减少网络请求;对不同分…...

尚医通-(三十三)就诊人管理功能实现

目录: (1)前台用户系统-就诊人管理-需求说明 (2)就诊人管理-接口开发-列表接口 (3)就诊人管理-接口开发-其他接口 (4)前台用户系统-就诊人管理-前端整合 &#xff0…...

《Spring Boot 趣味实战课》读书笔记(二)

牛刀小试——五分钟入门 Spring Boot 万物皆可 Hello World 创建一个 Web 工程 填写项目信息 选择依赖 从 IDEA 打开下载好的 Spring Boot 工程: 完成核心代码 创建 HelloController 类并编写 hello 方法 创建一个 HelloController 类,或者选择 Fi…...

Spring Cloud -- GateWay

为什么需要网关在微服务架构中,一个系统会被拆分为很多个微服务。那么作为客户端要如何去调用这么多的微服务呢?如果没有网关的存在,我们只能在客户端记录每个微服务的地址,然后分别去调用。这样的话会产生很多问题,例…...

【C语言】memcpy , memset等内存操作函数使用方法与注意事项

这个章节&#xff0c;我们探讨C语言内存操作函数。 重点介绍处理内存操作函数使用和注意事项 和内存函数如何模拟实现。 内存函数所需头文件 #include<string.h> 文章目录memcpymemcpy 函数模拟实现memmovememmove 函数模拟实现memcmpmemcmp 函数模拟实现memsetmemset 函…...

尚融宝04-mybatis-plus插件和条件构造器

目录 一、分页插件 1、添加配置类 2、添加分页插件 3、测试分页 二、XML自定义分页 1、UserMapper中定义接口方法 2、定义XML 3、测试 三、乐观锁 1、场景 2、乐观锁方案 3、乐观锁实现流程 4、优化流程 四、wapper介绍 1、Wrapper家族 2、创建测试类 五、Qu…...

面试重难点问题(C++)

持续更新&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 网络部分 1.问&#xff0c;四次挥手的过程&#xff0c;和双方状态变化&#xff1f; 挥手这前&#xff0c;两边都是established状态&#xff0c;客户端发起断开请求&#xff0c;向服务器发送fin请求&…...

androidx.appcompat 升级到1.5.1 趟过的坑

APP 要上google play&#xff0c;Android SDK 版本要升级到32&#xff1b;接了一个第三方SDK&#xff0c;不巧的是这个SDK引用appcompat是1.5.1&#xff0c;顺手把appcompat 包升级到1.5.1&#xff0c;这草率的一升&#xff0c;带来的不止一地鸡毛&#xff0c;还有精神上被残忍…...

[C++]反向迭代器

目录 前言&#xff1a; 1 对反向迭代器的构造思想 2 实现反向迭代器 3 完整代码 前言&#xff1a; 本篇文章主要介绍了STL容器当中的反向迭代器&#xff0c;可能有朋友会说&#xff1a;“反向迭代器有什么好学的&#xff1f;不一样还是迭代器吗&#xff0c;我正向能写出来&…...

解析Python编程中的包结构

假设你想设计一个模块集&#xff08;也就是一个“包”&#xff09;来统一处理声音文件和声音数据。通常由它们的扩展有不同的声音格式&#xff0c;例如&#xff1a;WAV&#xff0c;AIFF&#xff0c;AU&#xff09;&#xff0c;所以你可能需要创建和维护一个不断增长的各种文件格…...

【前端】深入浅出缓存原理

缓存的基本原理 对于前端来说&#xff0c;缓存主要分为浏览器缓存&#xff08;比如 localStorage、sessionStorage、cookie等等&#xff09;以及http缓存&#xff0c;也是本文主要讲述的。 当然叫法也不一样&#xff0c;比如客户端缓存大概包括浏览器缓存和http缓存 所谓htt…...

单调栈图文详解(附Java模板)

&#x1f34f;&#x1f350;&#x1f34a;&#x1f351;&#x1f352;&#x1f353;&#x1fad0;&#x1f951;&#x1f34b;&#x1f349;&#x1f95d; 啥是"单调栈"&#xff0c;它能解决什么样的问题&#xff1f; 文章目录&#x1f9a9;单调栈的概念&a…...

彻底理解Session、Cookie、Token,入门及实战

文章目录Session Cookie的使用Token的使用Session Cookie的使用 1. Session存储数据 HttpSession session request.getSession(); //Servlet底层通过的SESSIONID&#xff0c;获取Session对象。 session.setAttribute("loginTime",new Date()); out.println(&q…...

为什么运营商大数据可以精准获客?

“获客难”&#xff0c;“获客成本高”&#xff0c;一直是困扰企业的大问题&#xff0c;身边的许多朋友在吐槽客户的意向度不高&#xff0c;总是无法成交&#xff0c;员工非常积极主动去跟踪客户了&#xff0c;但始终事倍功半&#xff0c;这就像是老人们常说的一句老话“热脸贴…...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题&#xff1a;docker pull 失败 网络不同&#xff0c;需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...