大廠面試系列一些問題的答案

知識點總結

  • InnoDB的行鎖

    (1)共享鎖(S):用法lock in share mode,又稱讀鎖,允許一個事務去讀一行,阻止其他事務獲得相同數據集的排他鎖。若事務T對數據對象A加上S鎖,則事務T可以讀A但不能修改A,其他事務只能再對A加S鎖,而不能加X鎖,直到T釋放A上的S鎖。這保證了其他事務可以讀A,但在T釋放A上的S鎖之前不能對A做任何修改。 (2)排他鎖(X):用法for update,又稱寫鎖,允許獲取排他鎖的事務更新數據,阻止其他事務取得相同的數據集共享讀鎖和排他寫鎖。若事務T對數據對象A加上X鎖,事務T可以讀A也可以修改A,其他事務不能再對A加任何鎖,直到T釋放A上的鎖。在沒有索引的情況下,InnoDB只能使用表鎖

  • Spring的事務傳播級別

    (1)REQUIRED(默認):支持使用當前事務,如果當前事務不存在,創建一個新事務。 (2)SUPPORTS:支持使用當前事務,如果當前事務不存在,則不使用事務。 (3)MANDATORY:強制,支持使用當前事務,如果當前事務不存在,則拋出Exception。 (4)REQUIRES_NEW:創建一個新事務,如果當前事務存在,把當前事務掛起。 (5)NOT_SUPPORTED:無事務執行,如果當前事務存在,把當前事務掛起。 (6)NEVER:無事務執行,如果當前有事務則拋出Exception。 (7)NESTED:嵌套事務,如果當前事務存在,那麼在嵌套的事務中執行。如果當前事務不存在,則表現跟REQUIRED一樣。

  • Redis與Mysql雙寫一致性方案

先更新數據庫,再刪緩存。數據庫的讀操作的速度遠快於寫操作的,所以髒數據很難出現。可以對異步延時刪除策略,保證讀請求完成以後,再進行刪除操作

  • 索引B+樹的葉子節點都可以存哪些東西?

可能存儲的是整行數據,也有可能是主鍵的值。B+樹的葉子節點存儲了整行數據的是主鍵索引,也被稱之爲聚簇索引。而索引B+ Tree的葉子節點存儲了主鍵的值的是非主鍵索引,也被稱之爲非聚簇索引

  • 分代回收

(1)HotSpot JVM把年輕代分爲了三部分:1個Eden區和2個Survivor區(分別叫from和to)。一般情況下,新創建的對象都會被分配到Eden區(一些大對象特殊處理),這些對象經過第一次Minor GC後,如果仍然存活,將會被移到Survivor區。對象在Survivor區中每熬過一次Minor GC,年齡就會增加1歲,當它的年齡增加到一定程度時,就會被移動到年老代中。 (2)因爲年輕代中的對象基本都是朝生夕死的,所以在年輕代的垃圾回收算法使用的是複製算法,複製算法的基本思想就是將內存分爲兩塊,每次只用其中一塊,當這一塊內存用完,就將還活着的對象複製到另外一塊上面。複製算法不會產生內存碎片。 (3)在GC開始的時候,對象只會存在於Eden區和名爲“From”的Survivor區,Survivor區“To”是空的。緊接着進行GC,Eden區中所有存活的對象都會被複制到“To”,而在“From”區中,仍存活的對象會根據他們的年齡值來決定去向。年齡達到一定值(年齡閾值,可以通過-XX:MaxTenuringThreshold來設置)的對象會被移動到年老代中,沒有達到閾值的對象會被複制到“To”區域。經過這次GC後,Eden區和From區已經被清空。這個時候,“From”和“To”會交換他們的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎樣,都會保證名爲To的Survivor區域是空的。Minor GC會一直重複這樣的過程,直到“To”區被填滿,“To”區被填滿之後,會將所有對象移動到年老代中。

  • 線程之間如何通信?

(1)利用最基本的synchronized (2)利用synchronized、notify、wait (3)while輪詢的方式 (4)利用Lock和Condition (5)利用volatile (6)利用AtomicInteger (7)利用CyclicBarrier (8)利用PipedInputStream (9)利用BlockingQueue

  • redis併發競爭key的解決方案

(1)分佈式鎖+時間戳 (2)利用消息隊列

  • 如何避免(預防)死鎖?

破壞“請求和保持”條件:讓進程在申請資源時,一次性申請所有需要用到的資源,不要一次一次來申請,當申請的資源有一些沒空,那就讓線程等待。不過這個方法比較浪費資源,進程可能經常處於飢餓狀態。還有一種方法是,要求進程在申請資源前,要釋放自己擁有的資源。 破壞“不可搶佔”條件:允許進程進行搶佔,方法一:如果去搶資源,被拒絕,就釋放自己的資源。方法二:操作系統允許搶,只要你優先級大,可以搶到。 破壞“循環等待”條件:將系統中的所有資源統一編號,進程可在任何時刻提出資源申請,但所有申請必須按照資源的編號順序提出(指定獲取鎖的順序,順序加鎖)

  • 高併發系統的設計與實現

在開發高併發系統時有三把利器用來保護系統:緩存、降級和限流。 (1)緩存:緩存比較好理解,在大型高併發系統中,如果沒有緩存數據庫將分分鐘被爆,系統也會瞬間癱瘓。使用緩存不單單能夠提升系統訪問速度、提高併發訪問量,也是保護數據庫、保護系統的有效方式。大型網站一般主要是“讀”,緩存的使用很容易被想到。在大型“寫”系統中,緩存也常常扮演者非常重要的角色。比如累積一些數據批量寫入,內存裏面的緩存隊列(生產消費),以及HBase寫數據的機制等等也都是通過緩存提升系統的吞吐量或者實現系統的保護措施。甚至消息中間件,你也可以認爲是一種分佈式的數據緩存。 (2)降級:服務降級是當服務器壓力劇增的情況下,根據當前業務情況及流量對一些服務和頁面有策略的降級,以此釋放服務器資源以保證核心任務的正常運行。降級往往會指定不同的級別,面臨不同的異常等級執行不同的處理。根據服務方式:可以拒接服務,可以延遲服務,也有時候可以隨機服務。根據服務範圍:可以砍掉某個功能,也可以砍掉某些模塊。總之服務降級需要根據不同的業務需求採用不同的降級策略。主要的目的就是服務雖然有損但是總比沒有好。 (3)限流:限流可以認爲服務降級的一種,限流就是限制系統的輸入和輸出流量已達到保護系統的目的。一般來說系統的吞吐量是可以被測算的,爲了保證系統的穩定運行,一旦達到的需要限制的閾值,就需要限制流量並採取一些措施以完成限制流量的目的。比如:延遲處理,拒絕處理,或者部分拒絕處理等等。

  • 高併發系統的限流如何實現?

常見的限流算法有計數器、漏桶和令牌桶算法。漏桶算法在分佈式環境中消息中間件或者Redis都是可選的方案。發放令牌的頻率增加可以提升整體數據處理的速度,而通過每次獲取令牌的個數增加或者放慢令牌的發放速度和降低整體數據處理速度。而漏桶不行,因爲它的流出速率是固定的,程序處理速度也是固定的。

  • JVM有哪些回收算法

    1)引用計數法 (2)複製算法 (3)標記-清除算法 (4)標記-整理算法 (5)分代收集算法

  • 垃圾收集器有哪些?

(1)Serial收集器 (2)ParNew 收集器 (3)Parallel Scavenge 收集器 (4)Serial Old收集器 (5)Parallel Old收集器 (6)CMS收集器 (7)G1收集器 (8)ZGC

  • 常見的分佈式事務方案有哪些?

(1)兩階段提交方案 (2)eBay 事件隊列方案 (3)TCC 補償模式 (4)緩存數據最終一致性

  • 運行時數據區域(內存模型)

(1)程序計數器:程序計數器是一塊較小的內存空間,它可以看作是當前線程所執行的字節碼的行號指示器。在虛擬機的概念模型裏,字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成。是線程私有”的內存。 (2)Java虛擬機棧:與程序計數器一樣,Java虛擬機棧(Java Virtual Machine Stacks)也是線程私有的,它的生命週期與線程相同。虛擬機棧描述的是Java方法執行的內存模型:每個方法在執行的同時都會創建一個棧幀 ,用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。每一個方法從調用直至執行完成的過程,就對應着一個棧幀在虛擬機棧中入棧到出棧的過程。 (3)本地方法棧:本地方法棧(Native Method Stack)與虛擬機棧所發揮的作用是非常相似的,它們之間的區別不過是虛擬機棧爲虛擬機執行Java方法(也就是字節碼)服務,而本地方法棧則爲虛擬機使用到的Native方法服務。 (4)Java堆:對於大多數應用來說,Java堆是Java虛擬機所管理的內存中最大的一塊。Java堆是被所有線程共享的一塊內存區域,在虛擬機啓動時創建。此內存區域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這裏分配內存。 (5)方法區(1.8叫元數據):方法區與 Java 堆一樣,是各個線程共享的內存區域,它用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。

  • CAS操作ABA問題

如果在這段期間它的值曾經被改成了B,後來又被改回爲A,那CAS操作就會誤認爲它從來沒有被改變過。Java併發包爲了解決這個問題,提供了一個帶有標記的原子引用類“AtomicStampedReference”,它可以通過控制變量值的版本來保證CAS的正確性

  • 爲什麼選擇B+樹作爲索引結構?

(1)Hash索引:Hash索引底層是哈希表,哈希表是一種以key-value存儲數據的結構,所以多個數據在存儲關係上是完全沒有任何順序關係的,所以,對於區間查詢是無法直接通過索引查詢的,就需要全表掃描。所以,哈希索引只適用於等值查詢的場景。而B+ 樹是一種多路平衡查詢樹,所以他的節點是天然有序的(左子節點小於父節點、父節點小於右子節點),所以對於範圍查詢的時候不需要做全表掃描 (2)二叉查找樹:解決了排序的基本問題,但是由於無法保證平衡,可能退化爲鏈表。 (3)平衡二叉樹:通過旋轉解決了平衡的問題,但是旋轉操作效率太低。 (4)紅黑樹:通過捨棄嚴格的平衡和引入紅黑節點,解決了 AVL旋轉效率過低的問題,但是在磁盤等場景下,樹仍然太高,IO次數太多。 (5)B+樹:在B樹的基礎上,將非葉節點改造爲不存儲數據純索引節點,進一步降低了樹的高度;此外將葉節點使用指針連接成鏈表,範圍查詢更加高效。

  • 髒讀和幻讀是什麼?

(1)髒讀是指當一個事務正在訪問數據,並且對數據進行了修改。而這種修改還沒有提交到數據庫中,這時,另外一個事務也訪問了這個數據,然後使用了這個數據。 (2)幻讀是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的數據進行了修改,這種修改涉及到了表中的全部數據行。同時,第二個事務也修改了這個表中的數據,這種修改是向表中插入一行新數據。那麼,以後就會發生操作第一個事務的用戶發現表中還有沒有修改的數據行,就好像發生了幻覺一樣。

  • 如何解決Redis緩存雪崩,緩存穿透問題?

緩存雪崩: (1)使用 Redis 高可用架構:使用 Redis 集羣來保證 Redis 服務不會掛掉 (2)緩存時間不一致,給緩存的失效時間,加上一個隨機值,避免集體失效 (3)限流降級策略:有一定的備案,比如個性推薦服務不可用了,換成熱點數據推薦服務 緩存穿透: (1)在接口做校驗 (2)存null值(緩存擊穿加鎖) (3)布隆過濾器攔截: 將所有可能的查詢key 先映射到布隆過濾器中,查詢時先判斷key是否存在布隆過濾器中,存在才繼續向下執行,如果不存在,則直接返回。 布隆過濾器將值進行多次哈希bit存儲,布隆過濾器說某個元素在,可能會被誤判。布隆過濾器說某個元素不在,那麼一定不在。

  • Redis的持久化機制

redis爲了保證效率,數據緩存在了內存中,但是會週期性的把更新的數據寫入磁盤或者把修改操作寫入追加的記錄文件中,以保證數據的持久化。 Redis的持久化策略有兩種: (1)RDB:快照形式是直接把內存中的數據保存到一個dump的文件中,定時保存,保存策略。 當Redis需要做持久化時,Redis會fork一個子進程,子進程將數據寫到磁盤上一個臨時RDB文件中。當子進程完成寫臨時文件後,將原來的RDB替換掉。 (2)AOF:把所有的對Redis的服務器進行修改的命令都存到一個文件裏,命令的集合。 使用AOF做持久化,每一個寫命令都通過write函數追加到appendonly.aof中。aof的默認策略是每秒鐘fsync一次,在這種配置下,就算髮生故障停機,也最多丟失一秒鐘的數據。 缺點是對於相同的數據集來說,AOF的文件體積通常要大於RDB文件的體積。根據所使用的fsync策略,AOF的速度可能會慢於RDB。 Redis默認是快照RDB的持久化方式。

  • 三次握手

(1)第一次握手:建立連接時,客戶端發送syn包(syn=x)到服務器,並進入SYN_SENT狀態,等待服務器確認;SYN:同步序列編號(Synchronize Sequence Numbers)。 (2)第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=x+1),同時自己也發送一個SYN包(syn=y),即SYN+ACK包,此時服務器進入SYN_RECV狀態; (3)第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=y+1),此包發送完畢,客戶端和服務器進入ESTABLISHED(TCP連接成功)狀態,完成三次握手。

  • Kafka消息是採用Pull模式,還是Push模式?

Kafka最初考慮的問題是,customer應該從brokes拉取消息還是brokers將消息推送到consumer,也就是pull還push。在這方面,Kafka遵循了一種大部分消息系統共同的傳統的設計:producer將消息推送到broker,consumer從broker拉取消息。push模式下,當broker推送的速率遠大於consumer消費的速率時,consumer恐怕就要崩潰了。最終Kafka還是選取了傳統的pull模式。Pull模式的另外一個好處是consumer可以自主決定是否批量的從broker拉取數據。Pull有個缺點是,如果broker沒有可供消費的消息,將導致consumer不斷在循環中輪詢,直到新消息到達。爲了避免這點,Kafka有個參數可以讓consumer阻塞知道新消息到達。

  • HashMap相關

(1)在jdk1.8之後,HashMap除了數組+鏈表之外,引用了紅黑樹。需要說明對於引用了紅黑樹的 HashMap 如何put一個元素,以及鏈表是在何時轉化爲紅黑樹的。比如,首先需要知道這個元素落在哪一個數組裏,獲取hashcode後並不是對數組長度取餘來確定的,而是高低位異或求與來得到的。這個地方首先得知道異或求與是做什麼樣的運算的。 (2)之後說一下在HashMap中的實現,比如hashcode無符號右移16位後和原hashcode做異或運算,這相當於把hashcode的高16位拿過來和hashcode的低16位做異或運算,因爲無符號右移後前面說的16位都補零,這就是前面說的 "高低位異或“,進而是“求與”,和誰求與呢,和數組長度減1 求與。說到這裏起碼能夠證明你是看過源碼的,接下來說說你的思考,比如我們知道對於hashmap 初始化容量決定了數組大小,一般我們對於數組這個初始容量的設置是有規律的,它應該是 2^n 。這個初始容量的設置影響了HashMap的效率,那又涉及到影響HashMap效率的主要因素,比如初始容量和負載因子。

  • 手寫一個基於懶漢式的雙重檢測的單例。

(1)單例有三個比較關鍵的點,一是私有構造方法,避免外部new出對象;二是保證唯一性;三是提供一個全局訪問點。 (2)另外,懶漢式雙重檢測的實現方式 有三點需要注意的地方,一是全局訪問點必須是靜態的,外界使用可以通過類直接調用,二是在進入鎖之後還需要校驗,三是保存單例對象的私有變量一定要用volatile修飾,這個地方可以多說一些,比如volatile防止指令重排序,保證內存可見性(JVM層面和CPU層面可以分別說)。

  • RPC原理

(1)爲什麼會出現RPC? RPC(Remote Procedure Call Protocol)——遠程過程調用協議。 一般來說,自己寫程序然後本地調用,這種程序的特點是服務的消費方和提供方。當我們進入公司時,面對的很可能就是成千上萬的服務提供方,這時候就需要使用RPC來進行遠程服務調用。RPC將原來的本地調用轉變爲調用遠端的服務器上的方法,給系統的處理能力和吞吐量帶來了近似於無限制提升的可能。 (2)RPC的組成 ①客戶端:服務的調用方 ②客戶端存根:存放服務端的地址消息,再將客戶端的請求參數打包成網絡消息,③然後通過網絡遠程發送給服務方。 ④服務端:真正的服務提供者。 ⑤服務端存根:接收客戶端發送過來的消息,將消息解包,並調用本地的方法。

  • Redis緩存回收機制

(1)數據過期: ①定時刪除策略:Redis啓動一個定時器監控所有的key,一旦有過期的話就進行刪除(遍歷所有key,非常耗費CPU) ②惰性刪除策略:獲取key的時候判斷是否過期, 過期則進行刪除 Redis採用的方式:①(隨機抓取一部分key進行檢測)+② (2)內存淘汰: ①noeviction:當內存不足以容納新寫入數據時,新寫入操作會報錯。(Redis 默認策略) ②allkeys-lru:當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的 Key。(LRU推薦使用) ③allkeys-random:當內存不足以容納新寫入數據時,在鍵空間中,隨機移除某個 Key。 ④volatile-lru:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,移除最近最少使用的 Key。這種情況一般是把 Redis 既當緩存,又做持久化存儲的時候才用。 ⑤volatile-random:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,隨機移除某個 Key。 ⑥volatile-ttl:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,有更早過期時間的 Key 優先移除。不推薦。如果沒有對應的鍵,則回退到noeviction策略。

  • Redis主從同步

(1)主從複製作用 ①數據冗餘 ②故障恢復(服務冗餘) ③負載均衡 ④讀寫分離(主節點寫操作、從節點讀操作) (2)主從複製過程 ①連接建立階段 步驟1:保存主節點信息 步驟2:建立socket連接 步驟3:發送ping命令 步驟4:身份驗證 步驟5:發送從節點端口信息 ②數據同步階段 從節點向主節點發送psync命令 根據主從節點當前狀態的不同,可以分爲全量複製和部分複製 ③命令傳播階段 主從節點進入命令傳播階段;在這個階段主節點將自己執行的寫命令發送給從節點,從節點接收命令並執行,從而保證主從節點數據的一致性。 (3)介紹全量複製和部分複製 ①全量複製:用於初次複製或其他無法進行部分複製的情況,將主節點中的所有數據都發送給從節點,是一個非常重型的操作。 ②部分複製:用於網絡中斷等情況後的複製,只將中斷期間主節點執行的寫命令發送給從節點,與全量複製相比更加高效。需要注意的是,如果網絡中斷時間過長,導致主節點沒有能夠完整地保存中斷期間執行的寫命令,則無法進行部分複製,仍使用全量複製。 (4)主從複製缺點:故障恢復無法自動化;寫操作無法負載均衡;存儲能力受到單機的限制。

  • 爲什麼會有哨兵機制?

在主從複製的基礎上,哨兵實現了自動化的故障恢復。

  • 哨兵機制作用?

(1)監控(Monitoring):哨兵會不斷地檢查主節點和從節點是否運作正常。 (2)自動故障轉移(Automatic failover):當主節點不能正常工作時,哨兵會開始自動故障轉移操作,它會將失效主節點的其中一個從節點升級爲新的主節點,並讓其他從節點改爲複製新的主節點。 (3)配置提供者(Configuration provider):客戶端在初始化時,通過連接哨兵來獲得當前Redis服務的主節點地址。 (4)通知(Notification):哨兵可以將故障轉移的結果發送給客戶端。

  • 哨兵機制節點組成?

它由兩部分組成,哨兵節點和數據節點: (1)哨兵節點:哨兵系統由一個或多個哨兵節點組成,哨兵節點是特殊的redis節點,不存儲數據。 (2)數據節點:主節點和從節點都是數據節點。

  • 哨兵機制原理?

(1)定時任務:每個哨兵節點維護了3個定時任務。定時任務的功能分別如下:通過向主從節點發送info命令獲取最新的主從結構;通過發佈訂閱功能獲取其他哨兵節點的信息;通過向其他節點發送ping命令進行心跳檢測,判斷是否下線。 (2)主觀下線:在心跳檢測的定時任務中,如果其他節點超過一定時間沒有回覆,哨兵節點就會將其進行主觀下線。顧名思義,主觀下線的意思是一個哨兵節點“主觀地”判斷下線;與主觀下線相對應的是客觀下線。 (3)客觀下線:哨兵節點在對主節點進行主觀下線後,會通過sentinel is-master-down-by-addr命令詢問其他哨兵節點該主節點的狀態;如果判斷主節點下線的哨兵數量達到一定數值,則對該主節點進行客觀下線。 (4)選舉領導者哨兵節點:當主節點被判斷客觀下線以後,各個哨兵節點會進行協商,選舉出一個領導者哨兵節點,並由該領導者節點對其進行故障轉移操作。監視該主節點的所有哨兵都有可能被選爲領導者,選舉使用的算法是Raft算法;Raft算法的基本思路是先到先得:即在一輪選舉中,哨兵A向B發送成爲領導者的申請,如果B沒有同意過其他哨兵,則會同意A成爲領導者。選舉的具體過程這裏不做詳細描述,一般來說,哨兵選擇的過程很快,誰先完成客觀下線,一般就能成爲領導者。 (5)故障轉移:選舉出的領導者哨兵,開始進行故障轉移操作,該操作大體可以分爲3個步驟: ①在從節點中選擇新的主節點:選擇的原則是,首先過濾掉不健康的從節點;然後選擇優先級最高的從節點(由slave-priority指定);如果優先級無法區分,則選擇複製偏移量最大的從節點;如果仍無法區分,則選擇runid最小的從節點。 ②更新主從狀態:通過slaveof no one命令,讓選出來的從節點成爲主節點;並通過slaveof命令讓其他節點成爲其從節點。 ③將已經下線的主節點(即6379)設置爲新的主節點的從節點,當6379重新上線後,它會成爲新的主節點的從節點。

  • 哨兵機制缺點

寫操作無法負載均衡;存儲能力受到單機的限制。(Redis集羣解決了該情況)

  • Zookeeper鎖是如何實現的?

一般使用Curator進行使用Zookeeper鎖,例如有兩個客戶端A和客戶端B,首先A先在鎖節點下創建例如01子節點的鎖,然後再獲取節點信息,發現自己的01節點排名第一,那麼就獲得鎖。 客戶端B也需要獲取鎖,現在鎖節點下創建例如02的子節點,然後再獲取鎖節點信息,發現鎖節點信息爲[01,02],並不排第一,因此獲取不到鎖,客戶端B會在他的順序節點的上一個順序節點加一個監聽器。 當客戶端A使用完鎖,刪除01節點,客戶端B獲取到01刪除的監聽,然後發現自己的02節點排名第一,那麼就獲取到鎖。

  • JVM內存模型

(1)程序計數器:線程私有,用來程序跳轉,流程控制 (2)方法區(1.8叫元數據區):線程共享,用於存儲類信息、常量、靜態變量等信息 (3)Java虛擬機棧:線程私有,用於方法調用Java 虛擬機棧會出現兩種錯誤:StackOverFlowError 和 OutOfMemoryError (4)堆:線程私有,主要的內存區域,存儲對象實例,垃圾回收主要針對這一塊。 (5)本地方法棧:線程共享,本地方法被執行的時候,在本地方法棧也會創建一個棧幀,用於存放該本地方法的局部變量表、操作數棧、動態鏈接、出口信息。

  • G1和CMS垃圾回收器

G1和CMS垃圾回收器 (1)CMS收集器:是一種以獲取最短回收停頓時間爲目標的收集器。 過程: ①初始標記:標記GC Roots能直接關聯到的對象,需要在safepoint位置暫停所有執行線程。---->STW ②併發標記:進行GC Roots Tracing,遍歷完從root可達的所有對象。該階段與工作線程併發執行。 ③重新標記:修正併發標記期間因用戶程序繼續運作而導致標記產生表動的那一部分對象的標記記錄。需要在safepoint位置暫停所有執行線程。--->STW ④併發清除: 開啓用戶線程,同時 GC 線程開始對爲標記的區域做清掃。 優點:併發收集、低停頓。 缺點: ①CMS收集器對CPU資源非常敏感。 ②CMS收集器無法處理浮動垃圾(Floating Garbage)。 ③CMS收集器是基於標記-清除算法,該算法缺點都有:標記和清除效率低/產生大量不連續的內存碎片。 ④停頓時間是不可預期的。 (2)G1收集器:重新定義了堆空間,打破了原有的分代模型,將堆劃分爲一個個區域。這麼做的目的是在進行收集時不必在全堆範圍內進行,這是它最顯著的特點。 過程: ①初始標記:標記GC Roots 可以直接關聯的對象,該階段需要線程停頓但是耗時短。---->STW ②併發標記:尋找存活的對象,可以與其他程序併發執行,耗時較長。 ③最終標記:併發標記期間用戶程序會導致標記記錄產生變動(好比一個阿姨一邊清理垃圾,另一個人一邊扔垃圾)虛擬機會將這段時間的變化記錄在Remembered Set Logs 中。最終標記階段會向Remembered Set合併併發標記階段的變化。這個階段需要線程停頓,也可以併發執行---->STW ④篩選回收:對每個Region的回收成本進行排序,按照用戶自定義的回收時間來制定回收計劃 優點: ①空間整合:G1使用Region獨立區域概念,G1利用的是標記複製法,不會產生垃圾碎片 ②分代收集:G1可以自己管理新生代和老年代 ③並行於併發:G1可以通過機器的多核來併發處理 STW停頓,減少停頓時間,並且可不停頓java線程執行GC動作,可通過併發方式讓GC和java程序同時執行。 ④可預測停頓:G1除了追求停頓時間,還建立了可預測停頓時間模型,能讓制定的M毫秒時間片段內,消耗在垃圾回收器上的時間不超過N毫秒 缺點: G1 需要記憶集 (具體來說是卡表)來記錄新生代和老年代之間的引用關係,這種數據結構在 G1 中需要佔用大量的內存,可能達到整個堆內存容量的 20% 甚至更多。而且 G1 中維護記憶集的成本較高,帶來了更高的執行負載,影響效率。

  • wait/await和sleep區別

(1)兩者最主要的區別在於:sleep 方法沒有釋放鎖,而 wait 方法釋放了鎖 。 (2)兩者都可以暫停線程的執行。 (3)wait 通常被用於線程間交互/通信,sleep 通常被用於暫停執行。 (4)wait() 方法被調用後,線程不會自動甦醒,需要別的線程調用同一個對象上的 notify() 或者 notifyAll() 方法。sleep() 方法執行完成後,線程會自動甦醒。或者可以使用 wait(long timeout)超時後線程會自動甦醒。

  • Redis的LRU過期策略的具體實現 Redis的LRU具體實現

用棧的形式會導致執行select *的時候大量非熱點數據佔領頭部數據,所以需要改進。 Redis每次按key獲取一個值的時候,都會更新value中的lru字段爲當前秒級別的時間戳。Redis初始的實現算法很簡單,隨機從dict中取出五個key,淘汰一個lru字段值最小的。 在3.0的時候,又改進了一版算法,首先第一次隨機選取的key都會放入一個pool中(pool的大小爲16),pool中的key是按lru大小順序排列的。接下來每次隨機選取的keylru值必須小於pool中最小的lru纔會繼續放入,直到將pool放滿。放滿之後,每次如果有新的key需要放入,需要將pool中lru最大的一個key取出。淘汰的時候,直接從pool中選取一個lru最小的值然後將其淘汰。

  • 哪些對象可以作爲GC Roots?

(1)虛擬機棧(棧幀中的本地變量表)中引用的對象。 (2)方法區中類靜態屬性引用的對象。 (3)方法區中常量引用的對象。 (4)本地方法棧中JNI(即一般說的Native方法)引用的對象。

  • ConcurrentHashMap的數據結構

在JDK1.7版本中,ConcurrentHashMap維護了一個Segment數組,Segment這個類繼承了重入鎖ReentrantLock,並且該類裏面維護了一個 HashEntry<K,V>[] table數組,在寫操作put,remove,擴容的時候,會對Segment加鎖,所以僅僅影響這個Segment,不同的Segment還是可以併發的,所以解決了線程的安全問題,同時又採用了分段鎖也提升了併發的效率。在JDK1.8版本中,ConcurrentHashMap摒棄了Segment的概念,而是直接用Node數組+鏈表+紅黑樹的數據結構來實現,併發控制使用Synchronized和CAS來操作,整個看起來就像是優化過且線程安全的HashMap。

  • tcp和udp的優點與缺點

(1)TCP的優點: 可靠,穩定 TCP的可靠體現在TCP在傳遞數據之前,會有三次握手來建立連接,而且在數據傳遞時,有確認、窗口、重傳、擁塞控制機制,在數據傳完後,還會斷開連接用來節約系統資源。 (2)TCP的缺點: 慢,效率低,佔用系統資源高,易被攻擊 TCP在傳遞數據之前,要先建連接,這會消耗時間,而且在數據傳遞時,確認機制、重傳機制、擁塞控制機制等都會消耗大量的時間,而且要在每臺設備上維護所有的傳輸連接,事實上,每個連接都會佔用系統的CPU、內存等硬件資源。 而且,因爲TCP有確認機制、三次握手機制,這些也導致TCP容易被人利用,實現DOS、DDOS、CC等攻擊。 (3)UDP的優點: 快,比TCP稍安全 UDP沒有TCP的握手、確認、窗口、重傳、擁塞控制等機制,UDP是一個無狀態的傳輸協議,所以它在傳遞數據時非常快。沒有TCP的這些機制,UDP較TCP被攻擊者利用的漏洞就要少一些。但UDP也是無法避免攻擊的,比如:UDP Flood攻擊…… (4)UDP的缺點: 不可靠,不穩定 因爲UDP沒有TCP那些可靠的機制,在數據傳遞時,如果網絡質量不好,就會很容易丟包。 基於上面的優缺點,那麼: 什麼時候應該使用TCP: 當對網絡通訊質量有要求的時候,比如:整個數據要準確無誤的傳遞給對方,這往往用於一些要求可靠的應用,比如HTTP、HTTPS、FTP等傳輸文件的協議,POP、SMTP等郵件傳輸的協議。 在日常生活中,常見使用TCP協議的應用如下: 瀏覽器,用的HTTP FlashFXP,用的FTP Outlook,用的POP、SMTP Putty,用的Telnet、SSH QQ文件傳輸。什麼時候應該使用UDP: 當對網絡通訊質量要求不高的時候,要求網絡通訊速度能儘量的快,這時就可以使用UDP。 比如,日常生活中,常見使用UDP協議的應用如下: QQ語音 QQ視頻 TFTP。

  • HashMap原理

(1)hashMap 是非線程安全的, hashMap 1.7的底層實現爲數組(table[])+鏈表(LinkList-->Entry),hashmap 1.8底層爲數組+鏈表/紅黑樹(當鏈表長度到達閾值TREEIFY_THRESHOLD(默認爲8)時,會轉化爲紅黑樹)

  • HashMap的put和resize的過程

(1)put過程: ①查看數組是否需要初始化 ②根據key計算hashcode ③根據hashcode計算出桶位置 ④遍歷鏈表,查看key值與鏈表節點的key值是否相等,如果相等的話,那麼進行覆蓋舊值,並返回舊值。1.8的話需要先查看鏈表長度是否達到閾值,如果達到閾值,先進行紅黑樹轉化然後再進行檢查擴容。 ⑤新增的時候需要檢查是否需要擴容,需要擴容的話進行兩倍擴容,擴容完成後進行插入新值。 (2)resize過程: resize擴容需要從四個方面來進行回答: ①什麼時候觸發resize? 當容量超過當前容量(默認容量16)乘以負載因子(默認0.75)就會進行擴容,擴容大小爲當前大小的兩倍(擴展問題,爲啥是兩倍:通過限制length是一個2的冪數,h & (length-1)和h % length結果是一致的)。 ②resize是如何hash的:h & (length-1) ③resize是如何進行鏈表操作的:使用頭插法進行數據插入,每次新put的值放在頭部 ④併發操作下,鏈表是如何成環的:HashMap的環:若當前線程此時獲得ertry節點,但是被線程中斷無法繼續執行,此時線程二進入transfer函數,並把函數順利執行,此時新表中的某個位置有了節點,之後線程一獲得執行權繼續執行,因爲併發transfer,所以兩者都是擴容的同一個鏈表,當線程一執行到e.next = new table[i] 的時候,由於線程二之前數據遷移的原因導致此時new table[i] 上就有ertry存在,所以線程一執行的時候,會將next節點,設置爲自己,導致自己互相使用next引用對方,因此產生鏈表,導致死循環。

  • 線程池有哪些類型

①FixedThreadPool:創建可重用固定線程數的線程池。 ②SingleThreadPool:創建只有一個線程的線程池。 ③CachedThreadPool:一個可根據需要創建新線程的線程池,如果現有線程沒有可用的,則創建一個新線程並添加到池中,如果有被使用完但是還沒銷燬的線程,就複用該線程。終止並從緩存中移除那些已有 60 秒鐘未被使用的線程。因此,長時間保持空閒的線程池不會使用任何資源。 ④ScheduledThreadPool:創建一個線程池,它可安排在給定延遲後運行命令或者定期地執行。

  • ConcurrentHashMap分段鎖原理

(1)ConcurrentHashMap採用了分段鎖技術,其中Segement繼承了RecentLock,當ConcurrentHashMap進行get、put操作時,均是同步的。各個Segement之間的get、put操作可以進行併發,即當一個線程訪問ConcurrentHashMap的Segement時,不會影響對其他Segement的訪問。

  • B-樹和B+樹區別

1)B-樹和B樹是一個概念,是多路搜索樹(相比於二叉搜索樹,IO次數更少)。B-樹的特性: ①關鍵字集合分佈在整顆樹中; ②任何一個關鍵字出現且只出現在一個結點中; ③搜索有可能在非葉子結點結束; ④其搜索性能等價於在關鍵字全集內做一次二分查找; ⑤其最底搜索性能爲O(logN) (2)B+樹是B-樹的變體,也是一種多路搜索樹 B+的特性: ①所有關鍵字都出現在葉子結點的鏈表中(稠密索引),且鏈表中的關鍵字恰好是有序的; ②不可能在非葉子結點命中; ③非葉子結點相當於是葉子結點的索引(稀疏索引),葉子結點相當於是存儲(關鍵字)數據的數據層; ④更適合文件索引系統; (3)B+樹的優勢: ①單一節點存儲更多的元素,使得查詢的IO次數更少。 ②所有查詢都要查找到葉子節點,查詢性能穩定。 ③所有葉子節點形成有序鏈表,便於範圍查詢。

  • Mysql數據庫索引原理

(1)MyISAM索引實現:MyISAM引擎使用B+Tree作爲索引結構,葉節點的data域存放的是數據記錄的地址。 (2)Innodb索引實現: ①第一個重大區別是InnoDB的數據文件本身就是索引文件。MyISAM索引文件和數據文件是分離的,索引文件僅保存數據記錄的地址。而在InnoDB中,表數據文件本身就是按B+Tree組織的一個索引結構,這棵樹的葉節點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,因此InnoDB表數據文件本身就是主索引。 ②第二個與MyISAM索引的不同是InnoDB的輔助索引data域存儲相應記錄主鍵的值而不是地址。換句話說,InnoDB的所有輔助索引都引用主鍵作爲data域。

  • 組合索引怎麼使用?最左匹配的原理。

1)組合索引怎麼使用? 例如組合索引(a,b,c),組合索引的生效原則是: 從前往後依次使用生效,如果中間某個索引沒有使用,那麼斷點前面(範圍值也算斷點,orderby不算斷點,用到索引)的索引部分起作用,斷點後面的索引沒有起作用; (2)最左匹配的原理:以最左邊的爲起點任何連續的索引都能匹配上

  • Spring生命週期

Bean 的生命週期概括起來就是 4 個階段: (1)實例化(Instantiation) (2)屬性賦值(Populate) (3)初始化(Initialization) (4)銷燬(Destruction)

  • Spring幾種scope區別?

(1)singleton:Spring的IOC容器中只有一個實例bean,該值爲scope的默認值 (2)prototype:每次getBean時都會創建一個新的實例 (3)request:每次請求都會創建一個實體bean (4)session:每次session請求時都會創建一個實體bean (5)globalsession:每個全局的HTTP Session,使用session定義的Bean都將產生一個新實例。

  • Spring AOP實現有哪幾種實現

接口代理和類代理會有什麼區別? (1)Spring AOP有兩種實現,均爲動態代理: ①JDK動態代理:基於反射進行動態代理,核心類是InvocationHandker類和Proxy類,被代理的類必須實現接口 ②CGLIB動態代理:被代理類無需實現接口,主要實現MethodInterceptor接口即可實現代理 (2)Spring AOP如果代理的類存在接口,優先使用JDK動態代理,否則使用CGLIB動態代理。
歡迎搜索關注本人與朋友共同開發的微信面經小程序【大廠面試助手】和公衆號【微瞰技術】

file
file

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章