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