圖解學(xué)習(xí)網(wǎng)站:https://xiaolincoding.com
大家好,我是小林。
春節(jié)還剩下幾天啦,應(yīng)該不少同學(xué)在返鄉(xiāng)的路上,祝愿大家返鄉(xiāng)順利,與家人團(tuán)聚迎新春。
昨天公眾號(hào)私信收到一個(gè)字節(jié)同學(xué)的投稿,他說(shuō)字節(jié)最近員工福利大調(diào)整,今年是最后一年新年紅包了,明年就再也沒(méi)有新年紅包的福利了。
調(diào)整前后對(duì)比圖,詳見(jiàn)下圖,取消了文化和年節(jié)禮品,多了一些健身房課程和洗牙報(bào)銷(xiāo)等福利。
新年紅包的金額標(biāo)準(zhǔn)根據(jù)在職時(shí)間分為五檔,具體數(shù)字在 288 元至 3688 元之間,與 2024 年紅包金額保持一致。
還有一點(diǎn),即使你在 2024 年離職了,到年底的時(shí)候,還是能領(lǐng)到字節(jié)發(fā)放的新年紅包的,這一點(diǎn)還是很不錯(cuò),中途離開(kāi)也并不會(huì)取消這項(xiàng)福利。
這兩年字節(jié)的新年紅包,其實(shí)相比 2022 年就有所減少,那時(shí)候的紅包金額是在 588 元至 6888 元之間,相當(dāng)于各檔次金額減半了。
春節(jié)紅包作為字節(jié)跳動(dòng)的一項(xiàng)傳統(tǒng)福利,一直備受員工期待。
曾經(jīng)也有個(gè)手握字節(jié)跳動(dòng)+阿里巴巴兩家大廠 offer的同學(xué),跟我說(shuō)他選擇加入字節(jié)的原因,是因?yàn)楫?dāng)初字節(jié) hr 說(shuō)字節(jié)員工福利比阿里好,比如有新年紅包、下午茶、節(jié)假日禮品這些。
結(jié)果入職一年之后,好消息是領(lǐng)到了字節(jié)春節(jié)紅包,壞消息是明年沒(méi)有了。
員工福利這些是隨著公司發(fā)展會(huì)有變化的,通常都是先福利砍半,然后再直接取消,所以選擇 offer 的時(shí)候,也不要過(guò)多在意員工福利這些,最重要的還是要看你每個(gè)月到手的薪資是多少,這個(gè)才是最實(shí)在的。
春節(jié)過(guò)后,就會(huì)迎來(lái)求職高峰期,26 屆校招的暑期實(shí)習(xí)招聘、25 屆校招的春招校園招聘、社招的金三銀四招聘,都會(huì)在節(jié)后陸陸續(xù)續(xù)開(kāi)展起來(lái)了,3 月份會(huì)迎來(lái)高峰期。
所以要抓住這一波機(jī)會(huì)的同學(xué),春節(jié)期間除了陪伴家里人之外,也抽一些時(shí)間備戰(zhàn)一下,而不是說(shuō)等 3 月份才開(kāi)始準(zhǔn)備,那肯定趕不上最佳的投遞的時(shí)間。
我也非常鼓勵(lì),校招多積累實(shí)習(xí)經(jīng)歷,有了實(shí)習(xí),秋招就會(huì)順利很多,26 屆的暑期實(shí)習(xí)招聘就是很關(guān)鍵的機(jī)會(huì),各大廠大概會(huì)在 2 月中旬左右陸續(xù),會(huì)持續(xù)到 7 月份,大家最好能在 3 月份之前準(zhǔn)備到可以面試的狀態(tài),3 月份就開(kāi)始投,這里也貼一下 25 屆春招實(shí)習(xí)的時(shí)間圖標(biāo)分析,可以看到 3 月份是最高峰的時(shí)候,持續(xù)到 7 月份。
那既然聊到字節(jié),按照老傳統(tǒng),這次來(lái)分享一位同學(xué)的字節(jié)一二三面面經(jīng)。
大家也可以在復(fù)習(xí)的時(shí)候,也可以評(píng)估自己如果這場(chǎng)面試是你自己來(lái)面,對(duì)于問(wèn)的內(nèi)容,你有把握不?如果有 60% 內(nèi)容你都有信心能說(shuō)出來(lái),那就可以開(kāi)始邊投邊加強(qiáng)了。
一二面八股比較多,三面主要問(wèn)項(xiàng)目去了,可惜沒(méi)穩(wěn)住,能走到三面已經(jīng)很不容易了,字節(jié)每一輪都有算法,面試強(qiáng)度還是比較大的。
我把這一二三面考察的知識(shí)點(diǎn)給大家羅列了 一下,可以很清晰看到,哪些知識(shí)點(diǎn)是重要的:
- Java:HashMap、異常、spring 循環(huán)依賴、設(shè)計(jì)模式、synchronizedmysql:索引、SQL優(yōu)化、b+樹(shù)操作系統(tǒng):進(jìn)程線程、軟鏈接和硬鏈接、io 多路復(fù)用、fork原理、堆棧區(qū)別網(wǎng)絡(luò):tcp 和 udp、https 握手、http2、斷點(diǎn)續(xù)傳、頭部字段手撕:算法(每一面都有)、單例模式(三面出現(xiàn))
一面八股
說(shuō)一下HashMap底層原理
從?JDK 1.7 和 JDK 1.8 版本區(qū)別回答:
在?JDK 1.7?版本之前, HashMap 數(shù)據(jù)結(jié)構(gòu)是數(shù)組和鏈表,HashMap通過(guò)哈希算法將元素的鍵(Key)映射到數(shù)組中的槽位(Bucket)。如果多個(gè)鍵映射到同一個(gè)槽位,它們會(huì)以鏈表的形式存儲(chǔ)在同一個(gè)槽位上,因?yàn)殒湵淼牟樵儠r(shí)間是O(n),所以沖突很?chē)?yán)重,一個(gè)索引上的鏈表非常長(zhǎng),效率就很低了。所以在?JDK 1.8?版本的時(shí)候做了優(yōu)化,當(dāng)一個(gè)鏈表的長(zhǎng)度超過(guò)8的時(shí)候就轉(zhuǎn)換數(shù)據(jù)結(jié)構(gòu),不再使用鏈表存儲(chǔ),而是使用紅黑樹(shù),查找時(shí)使用紅黑樹(shù),時(shí)間復(fù)雜度O(log n),可以提高查詢性能,但是在數(shù)量較少時(shí),即數(shù)量小于6時(shí),會(huì)將紅黑樹(shù)轉(zhuǎn)換回鏈表。
HashMap是否線程安全?
不是線程安全的,如果要保證線程安全,可以通過(guò)這些方法來(lái)保證:
- 使用同步代碼塊(synchronized)或同步方法來(lái)保護(hù)共享資源,確保在同一時(shí)刻只有一個(gè)線程訪問(wèn)。使用線程安全的集合類(lèi),如ConcurrentHashMap、CopyOnWriteArrayList等。使用Lock接口及其實(shí)現(xiàn)類(lèi)(如ReentrantLock)來(lái)進(jìn)行線程同步使用ThreadLocal來(lái)保證每個(gè)線程都有自己獨(dú)立的變量副本
HashMap為什么不安全?
- JDK 1.7 HashMap 采用數(shù)組 + 鏈表的數(shù)據(jù)結(jié)構(gòu),多線程背景下,在數(shù)組擴(kuò)容的時(shí)候,存在 Entry 鏈死循環(huán)和數(shù)據(jù)丟失問(wèn)題。JDK 1.8 HashMap 采用數(shù)組 + 鏈表 + 紅黑二叉樹(shù)的數(shù)據(jù)結(jié)構(gòu),優(yōu)化了 1.7 中數(shù)組擴(kuò)容的方案,解決了 Entry 鏈死循環(huán)和數(shù)據(jù)丟失問(wèn)題。但是多線程背景下,put 方法存在數(shù)據(jù)覆蓋的問(wèn)題。
HTTPS的過(guò)程?
傳統(tǒng)的 TLS 握手基本都是使用 RSA 算法來(lái)實(shí)現(xiàn)密鑰交換的,在將 TLS 證書(shū)部署服務(wù)端時(shí),證書(shū)文件其實(shí)就是服務(wù)端的公鑰,會(huì)在 TLS 握手階段傳遞給客戶端,而服務(wù)端的私鑰則一直留在服務(wù)端,一定要確保私鑰不能被竊取。
在 RSA 密鑰協(xié)商算法中,客戶端會(huì)生成隨機(jī)密鑰,并使用服務(wù)端的公鑰加密后再傳給服務(wù)端。根據(jù)非對(duì)稱加密算法,公鑰加密的消息僅能通過(guò)私鑰解密,這樣服務(wù)端解密后,雙方就得到了相同的密鑰,再用它加密應(yīng)用消息。
我用 Wireshark 工具抓了用 RSA 密鑰交換的 TLS 握手過(guò)程,你可以從下面看到,一共經(jīng)歷了四次握手:
TLS 第一次握手
首先,由客戶端向服務(wù)器發(fā)起加密通信請(qǐng)求,也就是 ClientHello 請(qǐng)求。在這一步,客戶端主要向服務(wù)器發(fā)送以下信息:
- (1)客戶端支持的 TLS 協(xié)議版本,如 TLS 1.2 版本。(2)客戶端生產(chǎn)的隨機(jī)數(shù)(Client Random),后面用于生成「會(huì)話秘鑰」條件之一。(3)客戶端支持的密碼套件列表,如 RSA 加密算法。
TLS 第二次握手
服務(wù)器收到客戶端請(qǐng)求后,向客戶端發(fā)出響應(yīng),也就是 SeverHello。服務(wù)器回應(yīng)的內(nèi)容有如下內(nèi)容:
- (1)確認(rèn) TLS 協(xié)議版本,如果瀏覽器不支持,則關(guān)閉加密通信。(2)服務(wù)器生產(chǎn)的隨機(jī)數(shù)(Server Random),也是后面用于生產(chǎn)「會(huì)話秘鑰」條件之一。(3)確認(rèn)的密碼套件列表,如 RSA 加密算法。(4)服務(wù)器的數(shù)字證書(shū)。
TLS 第三次握手
客戶端收到服務(wù)器的回應(yīng)之后,首先通過(guò)瀏覽器或者操作系統(tǒng)中的 CA 公鑰,確認(rèn)服務(wù)器的數(shù)字證書(shū)的真實(shí)性。
如果證書(shū)沒(méi)有問(wèn)題,客戶端會(huì)從數(shù)字證書(shū)中取出服務(wù)器的公鑰,然后使用它加密報(bào)文,向服務(wù)器發(fā)送如下信息:
- (1)一個(gè)隨機(jī)數(shù)(pre-master key)。該隨機(jī)數(shù)會(huì)被服務(wù)器公鑰加密。(2)加密通信算法改變通知,表示隨后的信息都將用「會(huì)話秘鑰」加密通信。(3)客戶端握手結(jié)束通知,表示客戶端的握手階段已經(jīng)結(jié)束。這一項(xiàng)同時(shí)把之前所有內(nèi)容的發(fā)生的數(shù)據(jù)做個(gè)摘要,用來(lái)供服務(wù)端校驗(yàn)。
上面第一項(xiàng)的隨機(jī)數(shù)是整個(gè)握手階段的第三個(gè)隨機(jī)數(shù),會(huì)發(fā)給服務(wù)端,所以這個(gè)隨機(jī)數(shù)客戶端和服務(wù)端都是一樣的。
服務(wù)器和客戶端有了這三個(gè)隨機(jī)數(shù)(Client Random、Server Random、pre-master key),接著就用雙方協(xié)商的加密算法,各自生成本次通信的「會(huì)話秘鑰」。
TLS 第四次握手
服務(wù)器收到客戶端的第三個(gè)隨機(jī)數(shù)(pre-master key)之后,通過(guò)協(xié)商的加密算法,計(jì)算出本次通信的「會(huì)話秘鑰」。
然后,向客戶端發(fā)送最后的信息:
- (1)加密通信算法改變通知,表示隨后的信息都將用「會(huì)話秘鑰」加密通信。(2)服務(wù)器握手結(jié)束通知,表示服務(wù)器的握手階段已經(jīng)結(jié)束。這一項(xiàng)同時(shí)把之前所有內(nèi)容的發(fā)生的數(shù)據(jù)做個(gè)摘要,用來(lái)供客戶端校驗(yàn)。
至此,整個(gè) TLS 的握手階段全部結(jié)束。接下來(lái),客戶端與服務(wù)器進(jìn)入加密通信,就完全是使用普通的 HTTP 協(xié)議,只不過(guò)用「會(huì)話秘鑰」加密內(nèi)容。
中間人劫持了會(huì)怎么樣?
客戶端通過(guò)瀏覽器向服務(wù)端發(fā)起 HTTPS 請(qǐng)求時(shí),被「假基站」轉(zhuǎn)發(fā)到了一個(gè)「中間人服務(wù)器」,于是客戶端是和「中間人服務(wù)器」完成了 TLS 握手,然后這個(gè)「中間人服務(wù)器」再與真正的服務(wù)端完成 TLS 握手。
具體過(guò)程如下:
-
- 客戶端向服務(wù)端發(fā)起 HTTPS 建立連接請(qǐng)求時(shí),然后被「假基站」轉(zhuǎn)發(fā)到了一個(gè)「中間人服務(wù)器」,接著中間人向服務(wù)端發(fā)起 HTTPS 建立連接請(qǐng)求,此時(shí)客戶端與中間人進(jìn)行 TLS 握手,中間人與服務(wù)端進(jìn)行 TLS 握手;在客戶端與中間人進(jìn)行 TLS 握手過(guò)程中,中間人會(huì)發(fā)送自己的公鑰證書(shū)給客戶端,
客戶端驗(yàn)證證書(shū)的真?zhèn)?/strong>
- ,然后從證書(shū)拿到公鑰,并生成一個(gè)隨機(jī)數(shù),用公鑰加密隨機(jī)數(shù)發(fā)送給中間人,中間人使用私鑰解密,得到隨機(jī)數(shù),此時(shí)雙方都有隨機(jī)數(shù),然后通過(guò)算法生成對(duì)稱加密密鑰(A),后續(xù)客戶端與中間人通信就用這個(gè)對(duì)稱加密密鑰來(lái)加密數(shù)據(jù)了。在中間人與服務(wù)端進(jìn)行 TLS 握手過(guò)程中,服務(wù)端會(huì)發(fā)送從 CA 機(jī)構(gòu)簽發(fā)的公鑰證書(shū)給中間人,從證書(shū)拿到公鑰,并生成一個(gè)隨機(jī)數(shù),用公鑰加密隨機(jī)數(shù)發(fā)送給服務(wù)端,服務(wù)端使用私鑰解密,得到隨機(jī)數(shù),此時(shí)雙方都有隨機(jī)數(shù),然后通過(guò)算法生成對(duì)稱加密密鑰(B),后續(xù)中間人與服務(wù)端通信就用這個(gè)對(duì)稱加密密鑰來(lái)加密數(shù)據(jù)了。
- 后續(xù)的通信過(guò)程中,中間人用對(duì)稱加密密鑰(A)解密客戶端的 HTTPS 請(qǐng)求的數(shù)據(jù),然后用對(duì)稱加密密鑰(B)加密 HTTPS 請(qǐng)求后,轉(zhuǎn)發(fā)給服務(wù)端,接著服務(wù)端發(fā)送 HTTPS 響應(yīng)數(shù)據(jù)給中間人,中間人用對(duì)稱加密密鑰(B)解密 HTTPS 響應(yīng)數(shù)據(jù),然后再用對(duì)稱加密密鑰(A)加密后,轉(zhuǎn)發(fā)給客戶端。
從客戶端的角度看,其實(shí)并不知道網(wǎng)絡(luò)中存在中間人服務(wù)器這個(gè)角色。那么中間人就可以解開(kāi)瀏覽器發(fā)起的 HTTPS 請(qǐng)求里的數(shù)據(jù),也可以解開(kāi)服務(wù)端響應(yīng)給瀏覽器的 HTTPS 響應(yīng)數(shù)據(jù)。相當(dāng)于,中間人能夠 “偷看” 瀏覽器與服務(wù)端之間的 HTTPS 請(qǐng)求和響應(yīng)的數(shù)據(jù)。
但是要發(fā)生這種場(chǎng)景是有前提的,前提是用戶點(diǎn)擊接受了中間人服務(wù)器的證書(shū)。
中間人服務(wù)器與客戶端在 TLS 握手過(guò)程中,實(shí)際上發(fā)送了自己偽造的證書(shū)給瀏覽器,而這個(gè)偽造的證書(shū)是能被瀏覽器(客戶端)識(shí)別出是非法的,于是就會(huì)提醒用戶該證書(shū)存在問(wèn)題。
如果用戶執(zhí)意點(diǎn)擊「繼續(xù)瀏覽此網(wǎng)站」,相當(dāng)于用戶接受了中間人偽造的證書(shū),那么后續(xù)整個(gè) HTTPS 通信都能被中間人監(jiān)聽(tīng)了。
MySql索引是什么?
MySQL索引是數(shù)據(jù)庫(kù)表中的一種數(shù)據(jù)結(jié)構(gòu),可以提高數(shù)據(jù)檢索的速度。
索引存儲(chǔ)了指向表中數(shù)據(jù)的指針,這樣數(shù)據(jù)庫(kù)在查找數(shù)據(jù)時(shí)可以使用索引來(lái)快速定位到表中的特定行,而不必掃描整個(gè)表。使用索引可以顯著提高查詢的效率,特別是在處理大量數(shù)據(jù)時(shí)。
介紹一下索引有哪些類(lèi)型?
可以按照四個(gè)角度來(lái)分類(lèi)索引。
按「數(shù)據(jù)結(jié)構(gòu)」分類(lèi):B+tree索引、Hash索引、Full-text索引。
按「物理存儲(chǔ)」分類(lèi):聚簇索引(主鍵索引)、二級(jí)索引(輔助索引)。
按「字段特性」分類(lèi):主鍵索引、唯一索引、普通索引、前綴索引。
按「字段個(gè)數(shù)」分類(lèi):單列索引、聯(lián)合索引。
MySql索引底層是用的什么數(shù)據(jù)結(jié)構(gòu)?
從數(shù)據(jù)結(jié)構(gòu)的角度來(lái)看,MySQL 常見(jiàn)索引有 B+Tree 索引、HASH 索引、Full-Text 索引。
每一種存儲(chǔ)引擎支持的索引類(lèi)型不一定相同,我在表中總結(jié)了 MySQL 常見(jiàn)的存儲(chǔ)引擎 InnoDB、MyISAM 和 Memory 分別支持的索引類(lèi)型。
InnoDB 是在 MySQL 5.5 之后成為默認(rèn)的 MySQL 存儲(chǔ)引擎,B+Tree 索引類(lèi)型也是 MySQL 存儲(chǔ)引擎采用最多的索引類(lèi)型。
MySql查詢數(shù)據(jù)怎么優(yōu)化?
- 通過(guò) explain 執(zhí)行結(jié)果,查看 sql 是否走索引,如果不走索引,考慮增加索引??梢酝ㄟ^(guò)建立聯(lián)合索引,實(shí)現(xiàn)覆蓋索引優(yōu)化,減少回表聯(lián)合索引符合最左匹配原則,不然會(huì)索引失效避免索引失效,比如不要用左模糊匹配、函數(shù)計(jì)算、表達(dá)式計(jì)算等等。聯(lián)表查詢最好要以小表驅(qū)動(dòng)大表,并且被驅(qū)動(dòng)表的字段要有索引,當(dāng)然最好通過(guò)冗余字段的設(shè)計(jì),避免聯(lián)表查詢。針對(duì) limit n,y 深分頁(yè)的查詢優(yōu)化,可以把Limit查詢轉(zhuǎn)換成某個(gè)位置的查詢:select * from tb_sku where id>20000 limit 10;,該方案適用于主鍵自增的表,將字段多的表分解成多個(gè)表,有些字段使用頻率高,有些低,數(shù)據(jù)量大時(shí),會(huì)由于使用頻率低的存在而變慢,可以考慮分開(kāi)
spring循環(huán)依賴是怎么解決?
循環(huán)依賴指的是兩個(gè)類(lèi)中的屬性相互依賴對(duì)方:例如 A 類(lèi)中有 B 屬性,B 類(lèi)中有 A屬性,從而形成了一個(gè)依賴閉環(huán),如下圖。
循環(huán)依賴問(wèn)題在Spring中主要有三種情況:
- 第一種:通過(guò)構(gòu)造方法進(jìn)行依賴注入時(shí)產(chǎn)生的循環(huán)依賴問(wèn)題。第二種:通過(guò)setter方法進(jìn)行依賴注入且是在多例(原型)模式下產(chǎn)生的循環(huán)依賴問(wèn)題。第三種:通過(guò)setter方法進(jìn)行依賴注入且是在單例模式下產(chǎn)生的循環(huán)依賴問(wèn)題。
只有【第三種方式】的循環(huán)依賴問(wèn)題被 Spring 解決了,其他兩種方式在遇到循環(huán)依賴問(wèn)題時(shí),Spring都會(huì)產(chǎn)生異常。
Spring 解決單例模式下的setter循環(huán)依賴問(wèn)題的主要方式是通過(guò)三級(jí)緩存解決循環(huán)依賴。三級(jí)緩存指的是 Spring 在創(chuàng)建 Bean 的過(guò)程中,通過(guò)三級(jí)緩存來(lái)緩存正在創(chuàng)建的 Bean,以及已經(jīng)創(chuàng)建完成的 Bean 實(shí)例。具體步驟如下:
實(shí)例化 Bean:Spring 在實(shí)例化 Bean 時(shí),會(huì)先創(chuàng)建一個(gè)空的 Bean 對(duì)象,并將其放入一級(jí)緩存中。
屬性賦值:Spring 開(kāi)始對(duì) Bean 進(jìn)行屬性賦值,如果發(fā)現(xiàn)循環(huán)依賴,會(huì)將當(dāng)前 Bean 對(duì)象提前暴露給后續(xù)需要依賴的 Bean(通過(guò)提前暴露的方式解決循環(huán)依賴)。
初始化 Bean:完成屬性賦值后,Spring 將 Bean 進(jìn)行初始化,并將其放入二級(jí)緩存中。
注入依賴:Spring 繼續(xù)對(duì) Bean 進(jìn)行依賴注入,如果發(fā)現(xiàn)循環(huán)依賴,會(huì)從二級(jí)緩存中獲取已經(jīng)完成初始化的 Bean 實(shí)例。
通過(guò)三級(jí)緩存的機(jī)制,Spring 能夠在處理循環(huán)依賴時(shí),確保及時(shí)暴露正在創(chuàng)建的 Bean 對(duì)象,并能夠正確地注入已經(jīng)初始化的 Bean 實(shí)例,從而解決循環(huán)依賴問(wèn)題,保證應(yīng)用程序的正常運(yùn)行。
spring三級(jí)緩存的數(shù)據(jù)結(jié)構(gòu)是什么?
都是 Map類(lèi)型的緩存,比如Map {k:name; v:bean}。
一級(jí)緩存(Singleton Objects):這是一個(gè)Map類(lèi)型的緩存,存儲(chǔ)的是已經(jīng)完全初始化好的bean,即完全準(zhǔn)備好可以使用的bean實(shí)例。鍵是bean的名稱,值是bean的實(shí)例。這個(gè)緩存在DefaultSingletonBeanRegistry
類(lèi)中的singletonObjects
屬性中。
二級(jí)緩存(Early Singleton Objects):這同樣是一個(gè)Map類(lèi)型的緩存,存儲(chǔ)的是早期的bean引用,即已經(jīng)實(shí)例化但還未完全初始化的bean。這些bean已經(jīng)被實(shí)例化,但是可能還沒(méi)有進(jìn)行屬性注入等操作。這個(gè)緩存在DefaultSingletonBeanRegistry
類(lèi)中的earlySingletonObjects
屬性中。
三級(jí)緩存(Singleton Factories):這也是一個(gè)Map類(lèi)型的緩存,存儲(chǔ)的是ObjectFactory對(duì)象,這些對(duì)象可以生成早期的bean引用。當(dāng)一個(gè)bean正在創(chuàng)建過(guò)程中,如果它被其他bean依賴,那么這個(gè)正在創(chuàng)建的bean就會(huì)通過(guò)這個(gè)ObjectFactory來(lái)創(chuàng)建一個(gè)早期引用,從而解決循環(huán)依賴的問(wèn)題。這個(gè)緩存在DefaultSingletonBeanRegistry
類(lèi)中的singletonFactories
屬性中。
linux中軟鏈接和硬鏈接的區(qū)別?
有時(shí)候我們希望給某個(gè)文件取個(gè)別名,那么在 Linux 中可以通過(guò)硬鏈接(_Hard Link_)?和軟鏈接(_Symbolic Link_)?的方式來(lái)實(shí)現(xiàn),它們都是比較特殊的文件,但是實(shí)現(xiàn)方式也是不相同的。
硬鏈接是多個(gè)目錄項(xiàng)中的「索引節(jié)點(diǎn)」指向一個(gè)文件,也就是指向同一個(gè) inode,但是 inode 是不可能跨越文件系統(tǒng)的,每個(gè)文件系統(tǒng)都有各自的 inode 數(shù)據(jù)結(jié)構(gòu)和列表,所以硬鏈接是不可用于跨文件系統(tǒng)的。由于多個(gè)目錄項(xiàng)都是指向一個(gè) inode,那么只有刪除文件的所有硬鏈接以及源文件時(shí),系統(tǒng)才會(huì)徹底刪除該文件。
軟鏈接相當(dāng)于重新創(chuàng)建一個(gè)文件,這個(gè)文件有獨(dú)立的 inode,但是這個(gè)文件的內(nèi)容是另外一個(gè)文件的路徑,所以訪問(wèn)軟鏈接的時(shí)候,實(shí)際上相當(dāng)于訪問(wèn)到了另外一個(gè)文件,所以軟鏈接是可以跨文件系統(tǒng)的,甚至目標(biāo)文件被刪除了,鏈接文件還是在的,只不過(guò)指向的文件找不到了而已。
手撕代碼
- 鏈表向右循環(huán)K個(gè)數(shù)
一面感受
體驗(yàn)很好,面試官很會(huì)引導(dǎo)。面試官說(shuō)我準(zhǔn)備的很充分,快要結(jié)束時(shí)直接讓我準(zhǔn)備二面,說(shuō)一面直接給我過(guò)了。
二面八股
進(jìn)程和線程的區(qū)別?
本質(zhì)區(qū)別:進(jìn)程是操作系統(tǒng)資源分配的基本單位,而線程是任務(wù)調(diào)度和執(zhí)行的基本單位
在開(kāi)銷(xiāo)方面:每個(gè)進(jìn)程都有獨(dú)立的代碼和數(shù)據(jù)空間(程序上下文),程序之間的切換會(huì)有較大的開(kāi)銷(xiāo);線程可以看做輕量級(jí)的進(jìn)程,同一類(lèi)線程共享代碼和數(shù)據(jù)空間,每個(gè)線程都有自己獨(dú)立的運(yùn)行棧和程序計(jì)數(shù)器(PC),線程之間切換的開(kāi)銷(xiāo)小
穩(wěn)定性方面:進(jìn)程中某個(gè)線程如果崩潰了,可能會(huì)導(dǎo)致整個(gè)進(jìn)程都崩潰。而進(jìn)程中的子進(jìn)程崩潰,并不會(huì)影響其他進(jìn)程。
內(nèi)存分配方面:系統(tǒng)在運(yùn)行的時(shí)候會(huì)為每個(gè)進(jìn)程分配不同的內(nèi)存空間;而對(duì)線程而言,除了CPU外,系統(tǒng)不會(huì)為線程分配內(nèi)存(線程所使用的資源來(lái)自其所屬進(jìn)程的資源),線程組之間只能共享資源
包含關(guān)系:沒(méi)有線程的進(jìn)程可以看做是單線程的,如果一個(gè)進(jìn)程內(nèi)有多個(gè)線程,則執(zhí)行過(guò)程不是一條線的,而是多條線(線程)共同完成的;線程是進(jìn)程的一部分,所以線程也被稱為輕權(quán)進(jìn)程或者輕量級(jí)進(jìn)程
你說(shuō)到進(jìn)程是分配資源的基本單位,那么這個(gè)資源指的是什么?
虛擬內(nèi)存、文件句柄、信號(hào)量等資源。
說(shuō)一說(shuō)fork()原理?
主進(jìn)程在執(zhí)行 fork 的時(shí)候,操作系統(tǒng)會(huì)把主進(jìn)程的「頁(yè)表」復(fù)制一份給子進(jìn)程,這個(gè)頁(yè)表記錄著虛擬地址和物理地址映射關(guān)系,而不會(huì)復(fù)制物理內(nèi)存,也就是說(shuō),兩者的虛擬空間不同,但其對(duì)應(yīng)的物理空間是同一個(gè)。
這樣一來(lái),子進(jìn)程就共享了父進(jìn)程的物理內(nèi)存數(shù)據(jù)了,這樣能夠節(jié)約物理內(nèi)存資源,頁(yè)表對(duì)應(yīng)的頁(yè)表項(xiàng)的屬性會(huì)標(biāo)記該物理內(nèi)存的權(quán)限為只讀。
不過(guò),當(dāng)父進(jìn)程或者子進(jìn)程在向這個(gè)內(nèi)存發(fā)起寫(xiě)操作時(shí),CPU 就會(huì)觸發(fā)寫(xiě)保護(hù)中斷,這個(gè)寫(xiě)保護(hù)中斷是由于違反權(quán)限導(dǎo)致的,然后操作系統(tǒng)會(huì)在「寫(xiě)保護(hù)中斷處理函數(shù)」里進(jìn)行物理內(nèi)存的復(fù)制,并重新設(shè)置其內(nèi)存映射關(guān)系,將父子進(jìn)程的內(nèi)存讀寫(xiě)權(quán)限設(shè)置為可讀寫(xiě),最后才會(huì)對(duì)內(nèi)存進(jìn)行寫(xiě)操作,這個(gè)過(guò)程被稱為「寫(xiě)時(shí)復(fù)制(Copy On Write)」。
寫(xiě)時(shí)復(fù)制顧名思義,在發(fā)生寫(xiě)操作的時(shí)候,操作系統(tǒng)才會(huì)去復(fù)制物理內(nèi)存,這樣是為了防止 fork 創(chuàng)建子進(jìn)程時(shí),由于物理內(nèi)存數(shù)據(jù)的復(fù)制時(shí)間過(guò)長(zhǎng)而導(dǎo)致父進(jìn)程長(zhǎng)時(shí)間阻塞的問(wèn)題。
fork()會(huì)復(fù)制哪些東西?
- fork 階段會(huì)復(fù)制父進(jìn)程的頁(yè)表(虛擬內(nèi)存)fork 之后,如果發(fā)生了寫(xiě)時(shí)復(fù)制,就會(huì)復(fù)制物理內(nèi)存
堆和棧的區(qū)別?
分配方式:堆是動(dòng)態(tài)分配內(nèi)存,由程序員手動(dòng)申請(qǐng)和釋放內(nèi)存,通常用于存儲(chǔ)動(dòng)態(tài)數(shù)據(jù)結(jié)構(gòu)和對(duì)象。棧是靜態(tài)分配內(nèi)存,由編譯器自動(dòng)分配和釋放內(nèi)存,用于存儲(chǔ)函數(shù)的局部變量和函數(shù)調(diào)用信息。
內(nèi)存管理:堆需要程序員手動(dòng)管理內(nèi)存的分配和釋放,如果管理不當(dāng)可能會(huì)導(dǎo)致內(nèi)存泄漏或內(nèi)存溢出。棧由編譯器自動(dòng)管理內(nèi)存,遵循后進(jìn)先出的原則,變量的生命周期由其作用域決定,函數(shù)調(diào)用時(shí)分配內(nèi)存,函數(shù)返回時(shí)釋放內(nèi)存。
大小和速度:堆通常比棧大,內(nèi)存空間較大,動(dòng)態(tài)分配和釋放內(nèi)存需要時(shí)間開(kāi)銷(xiāo)。棧大小有限,通常比較小,內(nèi)存分配和釋放速度較快,因?yàn)槭蔷幾g器自動(dòng)管理。
說(shuō)說(shuō)io多路復(fù)用?
I/O 多路復(fù)用可以只使用一個(gè)進(jìn)程來(lái)維護(hù)多個(gè) Socket?。
一個(gè)進(jìn)程雖然任一時(shí)刻只能處理一個(gè)請(qǐng)求,但是處理每個(gè)請(qǐng)求的事件時(shí),耗時(shí)控制在 1 毫秒以內(nèi),這樣 1 秒內(nèi)就可以處理上千個(gè)請(qǐng)求,把時(shí)間拉長(zhǎng)來(lái)看,多個(gè)請(qǐng)求復(fù)用了一個(gè)進(jìn)程,這就是多路復(fù)用,這種思想很類(lèi)似一個(gè) CPU 并發(fā)多個(gè)進(jìn)程,所以也叫做時(shí)分多路復(fù)用。
select/poll/epoll 內(nèi)核提供給用戶態(tài)的多路復(fù)用系統(tǒng)調(diào)用,進(jìn)程可以通過(guò)一個(gè)系統(tǒng)調(diào)用函數(shù)從內(nèi)核中獲取多個(gè)事件。在獲取事件時(shí),先把所有連接(文件描述符)傳給內(nèi)核,再由內(nèi)核返回產(chǎn)生了事件的連接,然后在用戶態(tài)中再處理這些連接對(duì)應(yīng)的請(qǐng)求即可。
tcp與udp的區(qū)別
- 連接:TCP 是面向連接的傳輸層協(xié)議,傳輸數(shù)據(jù)前先要建立連接;UDP 是不需要連接,即刻傳輸數(shù)據(jù)。服務(wù)對(duì)象:TCP 是一對(duì)一的兩點(diǎn)服務(wù),即一條連接只有兩個(gè)端點(diǎn)。UDP 支持一對(duì)一、一對(duì)多、多對(duì)多的交互通信可靠性:TCP 是可靠交付數(shù)據(jù)的,數(shù)據(jù)可以無(wú)差錯(cuò)、不丟失、不重復(fù)、按序到達(dá)。UDP 是盡最大努力交付,不保證可靠交付數(shù)據(jù)。但是我們可以基于 UDP 傳輸協(xié)議實(shí)現(xiàn)一個(gè)可靠的傳輸協(xié)議,比如 QUIC 協(xié)議擁塞控制、流量控制:TCP 有擁塞控制和流量控制機(jī)制,保證數(shù)據(jù)傳輸的安全性。UDP 則沒(méi)有,即使網(wǎng)絡(luò)非常擁堵了,也不會(huì)影響 UDP 的發(fā)送速率。首部開(kāi)銷(xiāo):TCP 首部長(zhǎng)度較長(zhǎng),會(huì)有一定的開(kāi)銷(xiāo),首部在沒(méi)有使用「選項(xiàng)」字段時(shí)是 20 個(gè)字節(jié),如果使用了「選項(xiàng)」字段則會(huì)變長(zhǎng)的。UDP 首部只有 8 個(gè)字節(jié),并且是固定不變的,開(kāi)銷(xiāo)較小。傳輸方式:TCP 是流式傳輸,沒(méi)有邊界,但保證順序和可靠。UDP 是一個(gè)包一個(gè)包的發(fā)送,是有邊界的,但可能會(huì)丟包和亂序。應(yīng)用場(chǎng)景:TCP 是面向連接,能保證數(shù)據(jù)的可靠性交付,因此經(jīng)常用于:FTP、HTTP/HTTPS協(xié)議。UDP 面向無(wú)連接,它可以隨時(shí)發(fā)送數(shù)據(jù),再加上 UDP 本身的處理既簡(jiǎn)單又高效,經(jīng)常用于視頻、音頻等多媒體通信等。
說(shuō)說(shuō)HTTP頭部字段
Host?字段
客戶端發(fā)送請(qǐng)求時(shí),用來(lái)指定服務(wù)器的域名。
Host:?www.A.com
有了 Host 字段,就可以將請(qǐng)求發(fā)往「同一臺(tái)」服務(wù)器上的不同網(wǎng)站。
Content-Length 字段
服務(wù)器在返回?cái)?shù)據(jù)時(shí),會(huì)有 Content-Length 字段,表明本次回應(yīng)的數(shù)據(jù)長(zhǎng)度。
Content-Length:?1000
如上面則是告訴瀏覽器,本次服務(wù)器回應(yīng)的數(shù)據(jù)長(zhǎng)度是 1000 個(gè)字節(jié),后面的字節(jié)就屬于下一個(gè)回應(yīng)了。大家應(yīng)該都知道 HTTP 是基于 TCP 傳輸協(xié)議進(jìn)行通信的,而使用了 TCP 傳輸協(xié)議,就會(huì)存在一個(gè)“粘包”的問(wèn)題,HTTP 協(xié)議通過(guò)設(shè)置回車(chē)符、換行符作為 HTTP header 的邊界,通過(guò) Content-Length 字段作為 HTTP body 的邊界,這兩個(gè)方式都是為了解決“粘包”的問(wèn)題。
Connection 字段
Connection 字段最常用于客戶端要求服務(wù)器使用「HTTP 長(zhǎng)連接」機(jī)制,以便其他請(qǐng)求復(fù)用。
HTTP 長(zhǎng)連接的特點(diǎn)是,只要任意一端沒(méi)有明確提出斷開(kāi)連接,則保持 TCP 連接狀態(tài)。
HTTP/1.1 版本的默認(rèn)連接都是長(zhǎng)連接,但為了兼容老版本的 HTTP,需要指定 Connection 首部字段的值為 Keep-Alive。
Connection:?Keep-Alive
開(kāi)啟了 HTTP Keep-Alive 機(jī)制后, 連接就不會(huì)中斷,而是保持連接。當(dāng)客戶端發(fā)送另一個(gè)請(qǐng)求時(shí),它會(huì)使用同一個(gè)連接,一直持續(xù)到客戶端或服務(wù)器端提出斷開(kāi)連接。
Content-Type 字段
Content-Type 字段用于服務(wù)器回應(yīng)時(shí),告訴客戶端,本次數(shù)據(jù)是什么格式。
Content-Type:?text/html;?Charset=utf-8
上面的類(lèi)型表明,發(fā)送的是網(wǎng)頁(yè),而且編碼是UTF-8??蛻舳苏?qǐng)求的時(shí)候,可以使用 Accept 字段聲明自己可以接受哪些數(shù)據(jù)格式。
Accept:?*/*
上面代碼中,客戶端聲明自己可以接受任何格式的數(shù)據(jù)。
說(shuō)說(shuō)HTTP2
HTTP/2 相比 HTTP/1.1 性能上的改進(jìn):
頭部壓縮:HTTP/2 會(huì)壓縮頭(Header)如果你同時(shí)發(fā)出多個(gè)請(qǐng)求,他們的頭是一樣的或是相似的,那么,協(xié)議會(huì)幫你消除重復(fù)的部分。這就是所謂的 HPACK 算法:在客戶端和服務(wù)器同時(shí)維護(hù)一張頭信息表,所有字段都會(huì)存入這個(gè)表,生成一個(gè)索引號(hào),以后就不發(fā)送同樣字段了,只發(fā)送索引號(hào),這樣就提高速度了。
二進(jìn)制格式:HTTP/2 不再像 HTTP/1.1 里的純文本形式的報(bào)文,而是全面采用了二進(jìn)制格式,頭信息和數(shù)據(jù)體都是二進(jìn)制,并且統(tǒng)稱為幀(frame):頭信息幀(Headers Frame)和數(shù)據(jù)幀(Data Frame)。這樣雖然對(duì)人不友好,但是對(duì)計(jì)算機(jī)非常友好,因?yàn)橛?jì)算機(jī)只懂二進(jìn)制,那么收到報(bào)文后,無(wú)需再將明文的報(bào)文轉(zhuǎn)成二進(jìn)制,而是直接解析二進(jìn)制報(bào)文,這增加了數(shù)據(jù)傳輸?shù)男?/strong>。
并發(fā)傳輸:引出了 Stream 概念,多個(gè) Stream 復(fù)用在一條 TCP 連接。解決了HTTP/1.1 隊(duì)頭阻塞的問(wèn)題:服務(wù)器主動(dòng)推送資源:HTTP/2 還在一定程度上改善了傳統(tǒng)的「請(qǐng)求 - 應(yīng)答」工作模式,服務(wù)端不再是被動(dòng)地響應(yīng),可以主動(dòng)向客戶端發(fā)送消息。
手撕代碼
算法:求最長(zhǎng)無(wú)重復(fù)字符串智力題:64匹馬,8個(gè)賽道,如何找出最快的四匹?
二面感受
- 總結(jié):二面答得不太行,經(jīng)典智力題沒(méi)答出最優(yōu)方法(聽(tīng)說(shuō)字節(jié)面試官出智力題就是想掛人...)。
三面八股
java異常體系介紹一下?
Java異常類(lèi)層次結(jié)構(gòu)圖:
Java的異常體系主要基于兩大類(lèi):Throwable
類(lèi)及其子類(lèi)。Throwable
有兩個(gè)重要的子類(lèi):Error
和Exception
,它們分別代表了不同類(lèi)型的異常情況。
Error(錯(cuò)誤):表示運(yùn)行時(shí)環(huán)境的錯(cuò)誤。錯(cuò)誤是程序無(wú)法處理的嚴(yán)重問(wèn)題,如系統(tǒng)崩潰、虛擬機(jī)錯(cuò)誤、動(dòng)態(tài)鏈接失敗等。通常,程序不應(yīng)該嘗試捕獲這類(lèi)錯(cuò)誤。例如,OutOfMemoryError
、StackOverflowError
等。
Exception(異常):表示程序本身可以處理的異常條件。異常分為兩大類(lèi):
非運(yùn)行時(shí)異常:這類(lèi)異常在編譯時(shí)期就必須被捕獲或者聲明拋出。它們通常是外部錯(cuò)誤,如文件不存在(FileNotFoundException
)、類(lèi)未找到(ClassNotFoundException
)等。非運(yùn)行時(shí)異常強(qiáng)制程序員處理這些可能出現(xiàn)的問(wèn)題,增強(qiáng)了程序的健壯性。
運(yùn)行時(shí)異常:這類(lèi)異常包括運(yùn)行時(shí)異常(RuntimeException)和錯(cuò)誤(Error
)。運(yùn)行時(shí)異常由程序錯(cuò)誤導(dǎo)致,如空指針訪問(wèn)(NullPointerException
)、數(shù)組越界(ArrayIndexOutOfBoundsException
)等。運(yùn)行時(shí)異常是不需要在編譯時(shí)強(qiáng)制捕獲或聲明的。
java中的一些設(shè)計(jì)模式?介紹一下常用的一些?
我比較熟悉的是單例模式和工廠模式:
單例模式:確保一個(gè)類(lèi)只有一個(gè)實(shí)例,并提供一個(gè)全局訪問(wèn)點(diǎn)。
工廠方法模式:定義一個(gè)創(chuàng)建對(duì)象的接口,但讓實(shí)現(xiàn)這個(gè)接口的類(lèi)來(lái)決定實(shí)例化哪個(gè)類(lèi)。
抽象工廠模式:提供一個(gè)接口,用于創(chuàng)建相關(guān)或依賴對(duì)象的家族,而不需要指明具體類(lèi)。
除了用synchronized,還有什么方法可以實(shí)現(xiàn)線程同步?
使用ReentrantLock
類(lèi):ReentrantLock
是一個(gè)可重入的互斥鎖,相比synchronized
提供了更靈活的鎖定和解鎖操作。它還支持公平鎖和非公平鎖,以及可以響應(yīng)中斷的鎖獲取操作。
使用volatile
關(guān)鍵字:雖然volatile
不是一種鎖機(jī)制,但它可以確保變量的可見(jiàn)性。當(dāng)一個(gè)變量被聲明為volatile
后,線程將直接從主內(nèi)存中讀取該變量的值,這樣就能保證線程間變量的可見(jiàn)性。但它不具備原子性。
使用Atomic
類(lèi):Java提供了一系列的原子類(lèi),例如AtomicInteger
、AtomicLong
、AtomicReference
等,用于實(shí)現(xiàn)對(duì)單個(gè)變量的原子操作,這些類(lèi)在實(shí)現(xiàn)細(xì)節(jié)上利用了CAS(Compare-And-Swap)算法,可以用來(lái)實(shí)現(xiàn)無(wú)鎖的線程安全。
http 斷點(diǎn)重傳是什么?
斷點(diǎn)續(xù)傳是HTTP/1.1協(xié)議支持的特性。實(shí)現(xiàn)斷點(diǎn)續(xù)傳的功能,需要客戶端記錄下當(dāng)前的下載進(jìn)度,并在需要續(xù)傳的時(shí)候通知服務(wù)端本次需要下載的內(nèi)容片段。
一個(gè)最簡(jiǎn)單的斷點(diǎn)續(xù)傳流程如下:
- 客戶端開(kāi)始下載一個(gè)1024K的文件,服務(wù)端發(fā)送Accept-Ranges: bytes來(lái)告訴客戶端,其支持帶Range的請(qǐng)求假如客戶端下載了其中512K時(shí)候網(wǎng)絡(luò)突然斷開(kāi)了,過(guò)了一會(huì)網(wǎng)絡(luò)可以了,客戶端再下載時(shí)候,需要在HTTP頭中申明本次需要續(xù)傳的片段:Range:bytes=512000-這個(gè)頭通知服務(wù)端從文件的512K位置開(kāi)始傳輸文件,直到文件內(nèi)容結(jié)束服務(wù)端收到斷點(diǎn)續(xù)傳請(qǐng)求,從文件的512K位置開(kāi)始傳輸,并且在HTTP頭中增加:Content-Range:bytes 512000-/1024000,Content-Length: 512000。并且此時(shí)服務(wù)端返回的HTTP狀態(tài)碼應(yīng)該是206 Partial Content。如果客戶端傳遞過(guò)來(lái)的Range超過(guò)資源的大小,則響應(yīng)416 Requested Range Not Satisfiable
通過(guò)上面流程可以看出:斷點(diǎn)續(xù)傳中4個(gè)HTTP頭不可少的,分別是Range頭、Content-Range頭、Accept-Ranges頭、Content-Length頭。其中第一個(gè)Range頭是客戶端發(fā)過(guò)來(lái)的,后面3個(gè)頭需要服務(wù)端發(fā)送給客戶端。下面是它們的說(shuō)明:
Accept-Ranges:
bytes:這個(gè)值聲明了可被接受的每一個(gè)范圍請(qǐng)求, 大多數(shù)情況下是字節(jié)數(shù) bytes
Range: bytes=開(kāi)始位置-結(jié)束位置:Range是瀏覽器告知服務(wù)器所需分部分內(nèi)容范圍的消息頭。
手撕代碼
- 手撕?jiǎn)卫J绞炙核惴ǎ涸O(shè)計(jì)一個(gè)隊(duì)列,要求底層用數(shù)組,支持動(dòng)態(tài)擴(kuò)容
三面感受
- 三面面試官問(wèn)項(xiàng)目比較多,考察實(shí)際項(xiàng)目對(duì)自己的一些成長(zhǎng),面試官很好,只是我太菜了。