小型網(wǎng)站建設(shè)價格低網(wǎng)站人多怎么優(yōu)化
下午不準(zhǔn)備去圖書館自習(xí)來著,中午就狠狠地多睡了一個小時,三點(diǎn)起床靠在椅子上剝柚子,太爽了,這秋天的下午?!班]件:小米公司邀請你預(yù)約面試時間”..........
我擦,投了一個月了,認(rèn)真準(zhǔn)備的時候沒有面試,現(xiàn)在躺了,來了面了~~~預(yù)約到了第二天下午,怎么辦呢,考不考算法哇,考不考設(shè)計(jì)模式哇,考不考命令哇.....炸裂哇~
還能說什么呢,機(jī)會不是每次都有,還能怎么著,快學(xué),趕緊穿好衣服去自習(xí)室復(fù)習(xí)復(fù)習(xí),開卷
開面!
「面試官」自我介紹吧,別愣著了
「朱小哥」好的哥....
「面試官」說說你的項(xiàng)目背景,自己做的嗎
「朱小哥」不是我做的你做的嗎?這就是google的tcmalloc的mini版...
「面試官」malloc函數(shù)有什么問題呢,為什么需要改進(jìn),你的tcmalloc如何解決的
「朱小哥」主要就是針對多線程環(huán)境下帶來的鎖競爭的問題,現(xiàn)代很多的開發(fā)環(huán)境都是多核多線程,在申請內(nèi)存的場景下,必然存在激烈的鎖競爭問題。那malloc可不就不行了
tcmalloc針對鎖競爭這里采用的是,開始吟唱:
既然想要解決多線程環(huán)境下存在激烈的鎖競爭的問題,就得需要加鎖吧,可是如果加的鎖粒度太大,是非常影響效率的,所以要想盡可能的不加鎖或者少加鎖的話,再結(jié)合內(nèi)存池的這個思路,那我就可以實(shí)現(xiàn)一個分層的邏輯。
可以做一個多層架構(gòu),每一層針對不同大小的內(nèi)存分配請求使用不同的方法。這種靈活性有助于提高內(nèi)存的利用率和性能。
- 每一個ThreadCache是線程獨(dú)有的,這個地方不需要加鎖。我還使用到了一種TLS技術(shù),線程本地緩存,通過它我還可以在第一層就不需要加鎖,
- CentralCache層只有當(dāng)多個線程同時訪問central cache的同一個桶時才會存在鎖競爭,如果是多個線程同時訪問central cache的不同桶就不會存在鎖競爭。
- central cache的多個桶就可能同時向page cache申請內(nèi)存的,所以page cache也是存在線程安全問題的,因此在訪問page cache時也必須要加鎖。但是在page cache這里我們不能使用桶鎖,因?yàn)楫?dāng)central cache向page cache申請內(nèi)存時,page cache可能會將其他桶當(dāng)中大頁的span切小后再給central cache。
「面試官」threadcache初識的內(nèi)存是怎么拿到的,你是怎么拿到的threadcache的
「朱小哥」threadcache是怎么拿到內(nèi)存的?從對應(yīng)的哈希桶中去獲取哇!第一次申請當(dāng)然哈希桶中啥也沒有,你得去向下一層要,下一層中心緩存也沒有,就問PageCache要,PageCache沒有就會向堆申請128頁的內(nèi)存
「面試官」PageCache是如何解決內(nèi)存碎片的問題的
「朱小哥」內(nèi)碎片是設(shè)計(jì)的問題,解決不了,外碎片是一些空閑的連續(xù)內(nèi)存區(qū)域太小,這些內(nèi)存空間不連續(xù),以至于合計(jì)的內(nèi)存足夠,但是不能滿足一些的內(nèi)存分配申請需求。
關(guān)于如何解決外部碎片,聽好了!
- 采用一種各層遵守的規(guī)范,就是將不同大小的內(nèi)存塊分為不同的大小類別,每個大小類別包含一組相似大小的內(nèi)存塊。這樣可以更有效地分配和管理內(nèi)存,減少了內(nèi)存碎片。
- Thread-Caching 層維護(hù)了每個線程的本地內(nèi)存緩存,這意味著內(nèi)存塊通常在同一個線程內(nèi)進(jìn)行分配和釋放。這種局部性有助于減少內(nèi)存碎片,因?yàn)橥粋€線程通常會分配和釋放相似大小的內(nèi)存塊,從而減少了碎片化的可能性。
- 對于不用的內(nèi)存,也有必要來回收,實(shí)現(xiàn)了內(nèi)存塊的合并機(jī)制,可以合并相鄰的空閑內(nèi)存塊,從而減少內(nèi)存碎片。當(dāng)內(nèi)存塊被釋放時,tcmalloc會嘗試合并相鄰的空閑塊,以創(chuàng)建更大的可用內(nèi)存塊,提高內(nèi)存的利用率。
- 如果central cache釋放回一個span,則依次尋找span的前后page id的沒有在使用的空閑span,看是否可以合并,如果合并繼續(xù)向前尋找。這樣就可以將切小的內(nèi)存合并收縮成大的span,減少內(nèi)存碎片。
「面試官」PageCache這里是如何合并內(nèi)存的呢
「朱小哥」......
「面試官」你到底會不會啊
「朱小哥」忘了忘了
「面試官」噗~~~~,給你個雙向鏈表,你有三個節(jié)點(diǎn),a b c 如何刪除節(jié)點(diǎn)b,說說操作步驟
「朱小哥」小意思
「面試官」常見的進(jìn)程間通信的方式
「朱小哥」匿名管道、命令管道、消息隊(duì)列、共享內(nèi)存、信號、信號量、socket,你想讓我說說哪個~
「面試官」說說進(jìn)程間通信中的socket通信吧,使用socket進(jìn)行倆個主機(jī)通信的步驟
「朱小哥」王德發(fā)!!!選了個最不熟悉的,好你......Socket 實(shí)際上不僅用于不同的主機(jī)進(jìn)程間通信,還可以用于本地主機(jī)進(jìn)程間通信,可根據(jù)創(chuàng)建 Socket 的類型不同,分為三種常見的通信方式,一個是基于 TCP 協(xié)議的通信方式,一個是基于 UDP 協(xié)議的通信方式,一個是本地進(jìn)程間通信方式。
創(chuàng)建Socket:
通信的第一步是創(chuàng)建一個Socket對象,它充當(dāng)通信的端點(diǎn)。使用socket函數(shù)創(chuàng)建一個Socket。
綁定Socket:
為了讓Socket能夠在網(wǎng)絡(luò)上被其他計(jì)算機(jī)訪問,需要將其綁定到一個IP地址和端口號。bind函數(shù)用于綁定Socket。
監(jiān)聽(對于服務(wù)器):
如果你正在編寫服務(wù)器應(yīng)用程序,需要使用listen函數(shù)來等待客戶端的連接請求。
接受連接(對于服務(wù)器):
當(dāng)客戶端嘗試連接到服務(wù)器時,服務(wù)器使用accept函數(shù)來接受客戶端的連接請求,并創(chuàng)建一個新的Socket來處理與該客戶端的通信。
建立連接(對于客戶端):
客戶端使用connect函數(shù)來連接到服務(wù)器的Socket。
發(fā)送數(shù)據(jù):
使用send函數(shù)來將數(shù)據(jù)從一個Socket發(fā)送到另一個Socket。
接收數(shù)據(jù):
使用recv函數(shù)來從一個Socket接收數(shù)據(jù)。
關(guān)閉Socket:
當(dāng)通信結(jié)束時,使用close函數(shù)來關(guān)閉Socket連接。對于服務(wù)器端,還需要關(guān)閉原始的監(jiān)聽Socket。
這是Socket通信的基本流程??梢允褂胹ocket、bind、listen、accept、send、recv和close等系統(tǒng)調(diào)用函數(shù)來執(zhí)行這些操作。
「面試官」C++中的默認(rèn)構(gòu)造函數(shù)有哪些,移動構(gòu)造呢
「朱小哥」無參構(gòu)造、一般構(gòu)造、賦值運(yùn)算符重載、拷貝構(gòu)造函數(shù)、移動構(gòu)造(忘了)
「面試官」你給我寫一個拷貝構(gòu)造,什么場景使用拷貝構(gòu)造
「朱小哥」厚禮蟹!!!拷貝構(gòu)造怎么寫!!!
「面試官」這都不會,
「朱小哥」...
「面試官」拷貝構(gòu)造是淺拷貝還是深拷貝,說說深淺拷貝的區(qū)別
「朱小哥」軟了...,肯定是深拷貝哇
1. 淺拷貝, 又叫做值拷貝. 將源對象的值拷貝到目標(biāo)對象中去. 本質(zhì)上來說源對象和目標(biāo)對象共用同一份實(shí)體, 只是所引用的變量名不同, 指向的地址是相同的.
- 淺拷貝中, 如果目標(biāo)對象的值修改了, 則源對象的值也會相應(yīng)改變
2. 深拷貝, 拷貝的時候先開辟出和源對象大小一樣的空間, 然后將源對象里的內(nèi)容拷貝到目標(biāo)對象當(dāng)中去, 這樣倆個指針指向了不同的內(nèi)存位置, 并且里面的值是一樣的.
- 深拷貝的情況下, 不會出現(xiàn)重復(fù)釋放同一塊內(nèi)存的錯誤
- 深拷貝的實(shí)現(xiàn): 拷貝構(gòu)造和賦值運(yùn)算符重載實(shí)現(xiàn)
「面試官」指針和引用的區(qū)別
「朱小哥」簡單哇
- 指針是一個實(shí)體,需要分配內(nèi)存空間,引用只是個別名,不需要內(nèi)存空間
- 指針在定義的時候不一定要初始化,引用必須初始化,并且不能改變所引用的變量
- 指針可以時空指針,引用不能是空
- 有多級指針,沒有多級引用
- 引用的底層是指針
「面試官」一個類中是有成員變量和有構(gòu)造函數(shù)和析構(gòu)函數(shù)的,這些函數(shù)會不會占用內(nèi)存空間呢
「朱小哥」我覺得你說的有問題,成員函數(shù)是存放在代碼段的,當(dāng)然占用內(nèi)存,但是類對象的大小只包含成員變量,不包含成員函數(shù),會將類對象的地址傳遞給this指針,當(dāng)調(diào)用一個對象的非靜態(tài)成員函數(shù)時, 系統(tǒng)會把該對象的起始地址賦給成員函數(shù)的 this 指針, 另外,靜態(tài)成員函數(shù)不屬于任何一個對象, 所以靜態(tài)成員函數(shù)沒有 this 指針, 既然它沒有指向某一對象, 也就無法對一個對象中的非靜態(tài)成員進(jìn)行訪問
「面試官」memcpy和memmove使用過嗎,具體說說
「朱小哥」不會
memcpy 函數(shù)用于從一個內(nèi)存區(qū)域復(fù)制一定數(shù)量的字節(jié)到另一個內(nèi)存區(qū)域。它將源內(nèi)存區(qū)域的內(nèi)容按順序復(fù)制到目標(biāo)內(nèi)存區(qū)域,無論它們是否重疊。
void *memcpy(void *dest, const void *src, size_t n);
memmove 函數(shù)也用于從一個內(nèi)存區(qū)域復(fù)制一定數(shù)量的字節(jié)到另一個內(nèi)存區(qū)域,但與 memcpy 不同,它會處理重疊的內(nèi)存區(qū)域。memmove 會檢查內(nèi)存區(qū)域是否重疊,如果重疊,通過使用一個臨時緩沖區(qū),將源內(nèi)存區(qū)域的數(shù)據(jù)復(fù)制到這個緩沖區(qū),然后再將緩沖區(qū)的數(shù)據(jù)復(fù)制到目標(biāo)內(nèi)存區(qū)域。這種方法確保數(shù)據(jù)的正確復(fù)制而不會破壞原始數(shù)據(jù)。
void *memmove(void *dest, const void *src, size_t n);
由于需要使用額外的內(nèi)存來存儲臨時緩沖區(qū),所以 memmove 可能比 memcpy 性能稍差,但它保證了數(shù)據(jù)的完整性和正確性。
「面試官」呼~,反問吧,有什么就問
「朱小哥」組是做什么的
「面試官」巴拉巴拉........
「朱小哥」我可以進(jìn)入二面嗎
「面試官」別問,問就是回家等消息~,怎么說呢,回答還湊活,指針和引用是背八股的吧,簡直和網(wǎng)上回答是一樣,你不得理解理解?不能光背哇,背還背不熟.....拷貝構(gòu)造都不會寫就有點(diǎn)太離譜了
「朱小哥」面試結(jié)束我學(xué)還不行嘛,找個實(shí)習(xí)太難了