1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        今年后端爆了???

        共 7895字,需瀏覽 16分鐘

         ·

        2024-04-29 14:04

        大家好,我是二哥呀。

        每次登錄???,看到最多的就是各種 Java 后端崗位的喜訊,美團 OC了、快手 OC 了、就連騰訊 OC 的都是 Java 崗,我懷疑牛客是不是給我打了“只報喜不報憂”的標簽?

        星球里也有不少球友給我發(fā)來喜訊,難道說每年都在涼涼的 Java 后端又承擔起了就業(yè)的重任?!

        不可能,絕對不可能,這一切都是假象!反正我敢肯定,還有不少同學在嗷嗷叫,尤其是 24 屆春招還沒上岸的,25 屆沒找到暑期實習的(??)。

        我只能說拿到 offer 的,這個假期就放肆幾天吧;沒拿到的要不就茍一茍,繼續(xù)背背八股,優(yōu)化優(yōu)化簡歷?也許好運就要降臨到你頭上了。

        這次我們就以《Java 面試指南——攜程面經(jīng)》為例,繼續(xù)來看看攜程這家不錯的互聯(lián)網(wǎng)中廠面試官都喜歡問哪些問題,好做到知彼知己百戰(zhàn)不殆,我會用通俗易懂+手繪圖的方式,讓天下所有的面渣都能逆襲 ??

        二哥的 Java 面試指南

        內(nèi)容較長,建議正在沖刺 24 屆春招和 25 屆暑期實習、秋招的同學先收藏起來,面試的時候大概率會碰到,

        攜程面經(jīng)(詳細)

        對象創(chuàng)建到銷毀,內(nèi)存如何分配的,(類加載和對象創(chuàng)建過程,CMS,G1內(nèi)存清理和分配)

        當我們使用 new 關鍵字創(chuàng)建一個對象的時候,JVM 首先會檢查 new 指令的參數(shù)是否能在常量池中定位到一個類的符號引用,然后檢查這個符號引用代表的類是否已被加載、解析和初始化過。如果沒有,就先執(zhí)行相應的類加載過程。

        如果已經(jīng)加載,JVM 會為新生對象分配內(nèi)存,內(nèi)存分配完成之后,JVM 將分配到的內(nèi)存空間初始化為零值(成員變量,數(shù)值類型是 0,布爾類型是 false,對象類型是 null),接下來設置對象頭,對象頭里包含了對象是哪個類的實例、對象的哈希碼、對象的 GC 分代年齡等信息。

        最后,JVM 會執(zhí)行構造方法(<init>),將成員變量賦值為預期的值,這樣一個對象就創(chuàng)建完成了。

        二哥的 Java 進階之路:對象的創(chuàng)建過程

        對象的銷毀過程了解嗎?

        對象創(chuàng)建完成后,就可以通過引用來訪問對象的方法和屬性,當對象不再被任何引用指向時,對象就會變成垃圾。

        垃圾收集器會通過可達性分析算法判斷對象是否存活,如果對象不可達,就會被回收。

        垃圾收集器會通過標記清除、標記復制、標記整理等算法來回收內(nèi)存,將對象占用的內(nèi)存空間釋放出來。

        常用的垃圾收集器有 CMS、G1、ZGC 等,它們的回收策略和效率不同,可以根據(jù)具體的場景選擇合適的垃圾收集器。

        內(nèi)存如何分配的?

        在堆內(nèi)存分配對象時,主要使用兩種策略:指針碰撞和空閑列表。

        三分惡面渣逆襲:指針碰撞和空閑列表

        ①、指針碰撞(Bump the Pointer)

        假設堆內(nèi)存是一個連續(xù)的空間,分為兩個部分,一部分是已經(jīng)被使用的內(nèi)存,另一部分是未被使用的內(nèi)存。

        在分配內(nèi)存時,Java 虛擬機維護一個指針,指向下一個可用的內(nèi)存地址,每次分配內(nèi)存時,只需要將指針向后移動(碰撞)一段距離,然后將這段內(nèi)存分配給對象實例即可。

        ②、空閑列表(Free List)

        JVM 維護一個列表,記錄堆中所有未占用的內(nèi)存塊,每個空間塊都記錄了大小和地址信息。

        當有新的對象請求內(nèi)存時,JVM 會遍歷空閑列表,尋找足夠大的空間來存放新對象。

        分配后,如果選中的空閑塊未被完全利用,剩余的部分會作為一個新的空閑塊加入到空閑列表中。

        指針碰撞適用于管理簡單、碎片化較少的內(nèi)存區(qū)域(如年輕代),而空閑列表適用于內(nèi)存碎片化較嚴重或對象大小差異較大的場景(如老年代)。

        能詳細說一下 CMS 收集器的垃圾收集過程嗎?

        三分惡面渣逆襲:Concurrent Mark Sweep收集器運行示意圖

        CMS(Concurrent Mark Sweep)分 4 大步進行垃圾收集:

        • 初始標記(Initial Mark):標記所有從 GC Roots 直接可達的對象,這個階段需要 STW,但速度很快。
        • 并發(fā)標記(Concurrent Mark):從初始標記的對象出發(fā),遍歷所有對象,標記所有可達的對象。這個階段是并發(fā)進行的,STW。
        • 重新標記(Remark):完成剩余的標記工作,包括處理并發(fā)階段遺留下來的少量變動,這個階段通常需要短暫的 STW 停頓。
        • 并發(fā)清除(Concurrent Sweep):清除未被標記的對象,回收它們占用的內(nèi)存空間。

        G1 垃圾收集器了解嗎?

        G1 收集器的運行過程大致可劃分為這幾個步驟:

        ①、并發(fā)標記,G1 通過并發(fā)標記的方式找出堆中的垃圾對象。并發(fā)標記階段與應用線程同時執(zhí)行,不會導致應用線程暫停。

        ②、混合收集,在并發(fā)標記完成后,G1 會計算出哪些區(qū)域的回收價值最高(也就是包含最多垃圾的區(qū)域),然后優(yōu)先回收這些區(qū)域。這種回收方式包括了部分新生代區(qū)域和老年代區(qū)域。

        選擇回收成本低而收益高的區(qū)域進行回收,可以提高回收效率和減少停頓時間。

        ③、可預測的停頓,G1 在垃圾回收期間仍然需要「Stop the World」。不過,G1 在停頓時間上添加了預測機制,用戶可以 JVM 啟動時指定期望停頓時間,G1 會盡可能地在這個時間內(nèi)完成垃圾回收。

        三分惡面渣逆襲:G1收集器運行示意圖

        ThreadLocal,(作用,演進,軟指針,刪除過程)

        ThreadLocal 是 Java 中提供的一種用于實現(xiàn)線程局部變量的工具類。它允許每個線程都擁有自己的獨立副本,從而實現(xiàn)線程隔離,用于解決多線程中共享對象的線程安全問題。

        三分惡面渣逆襲:ThreadLocal線程副本

        ThreadLocal 本身并不存儲任何值,它只是作為一個映射,來映射線程的局部變量。當一個線程調(diào)用 ThreadLocal 的 set 或 get 方法時,實際上是訪問線程自己的 ThreadLocal.ThreadLocalMap。

        二哥的 Java 進階之路

        ThreadLocalMap 是 ThreadLocal 的靜態(tài)內(nèi)部類,它內(nèi)部維護了一個 Entry 數(shù)組,key 是 ThreadLocal 對象,value 是線程的局部變量本身。

        三分惡面渣逆襲:ThreadLoca結構圖

        早期的 ThreadLocal 不是這樣的,它的 ThreadLocalMap 中使用 Thread 作為 key,這也是最簡單的實現(xiàn)方式。

        黑馬:JDK 早期設計

        優(yōu)化后的方案有兩個好處,一個是 Map 中存儲的鍵值對變少了;另一個是 ThreadLocalMap 的生命周期和線程一樣長,線程銷毀的時候,ThreadLocalMap 也會被銷毀。

        Entry 繼承了 WeakReference,它限定了 key 是一個弱引用,弱引用的好處是當內(nèi)存不足時,JVM 會回收 ThreadLocal 對象,并且將其對應的 Entry 的 value 設置為 null,這樣在很大程度上可以避免內(nèi)存泄漏。

        使用完 ThreadLocal 后,及時調(diào)用 remove() 方法釋放內(nèi)存空間。

        try {
            threadLocal.set(value);
            // 執(zhí)行業(yè)務操作
        finally {
            threadLocal.remove(); // 確保能夠執(zhí)行清理
        }

        remove() 方法會將當前線程的 ThreadLocalMap 中的所有 key 為 null 的 Entry 全部清除。

        private void remove(ThreadLocal<?> key) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
            for (Entry e = tab[i];
                    e != null;
                    e = tab[i = nextIndex(i, len)]) {
                if (e.get() == key) {
                    e.clear();
                    expungeStaleEntry(i);
                    return;
                }
            }
        }

        public void clear() {
            this.referent = null;
        }

        線程上下文切換(我答的內(nèi)核態(tài)和用戶態(tài)切換時機,和切換需要加載哪些內(nèi)容)

        使用多線程的目的是為了充分利用 CPU,但是我們知道,并發(fā)其實是一個 CPU 來應付多個線程。

        三分惡面渣逆襲:線程切換

        為了讓用戶感覺多個線程是在同時執(zhí)行的, CPU 資源的分配采用了時間片輪轉也就是給每個線程分配一個時間片,線程在時間片內(nèi)占用 CPU 執(zhí)行任務。當線程使用完時間片后,就會處于就緒狀態(tài)并讓出 CPU 讓其他線程占用,這就是上下文切換。

        三分惡面渣逆襲:上下文切換時機

        cas和aba(原子操作+時間戳)

        CAS(Compare-and-Swap)是一種樂觀鎖的實現(xiàn)方式,全稱為“比較并交換”,是一種無鎖的原子操作。

        在 Java 中,我們可以使用 synchronized關鍵字和 CAS 來實現(xiàn)加鎖效果。

        synchronized 是悲觀鎖,盡管隨著 JDK 版本的升級,synchronized 關鍵字已經(jīng)“輕量級”了很多,但依然是悲觀鎖,線程開始執(zhí)行第一步就要獲取鎖,一旦獲得鎖,其他的線程進入后就會阻塞并等待鎖。

        CAS 是樂觀鎖,線程執(zhí)行的時候不會加鎖,它會假設此時沒有沖突,然后完成某項操作;如果因為沖突失敗了就重試,直到成功為止。

        在 CAS 中,有這樣三個值:

        • V:要更新的變量(var)
        • E:預期值(expected)
        • N:新值(new)

        比較并交換的過程如下:

        判斷 V 是否等于 E,如果等于,將 V 的值設置為 N;如果不等,說明已經(jīng)有其它線程更新了 V,于是當前線程放棄更新,什么都不做。

        這里的預期值 E 本質(zhì)上指的是“舊值”。

        這個比較和替換的操作是原子的,即不可中斷,確保了數(shù)據(jù)的一致性。

        什么是 ABA 問題?如何解決?

        如果一個位置的值原來是 A,后來被改為 B,再后來又被改回 A,那么進行 CAS 操作的線程將無法知曉該位置的值在此期間已經(jīng)被修改過。

        可以使用版本號/時間戳的方式來解決 ABA 問題。

        比如說,每次變量更新時,不僅更新變量的值,還更新一個版本號。CAS 操作時不僅要求值匹配,還要求版本號匹配。

        Java 的 AtomicStampedReference 類就實現(xiàn)了這種機制,它會同時檢查引用值和 stamp 是否都相等。

        二哥的 Java 進階之路:AtomicStampedReference

        volatile如何保證可見性(cup緩存和主緩存)

        當一個變量被聲明為 volatile 時,Java 內(nèi)存模型會確保所有線程看到該變量時的值是一致的。

        深入淺出 Java 多線程:Java內(nèi)存模型

        也就是說,當線程對 volatile 變量進行寫操作時,JMM 會在寫入這個變量之后插入一個 Store-Barrier(寫屏障)指令,這個指令會強制將本地內(nèi)存中的變量值刷新到主內(nèi)存中。

        三分惡面渣逆襲:volatile寫插入內(nèi)存屏障后生成的指令序列示意圖

        當線程對 volatile 變量進行讀操作時,JMM 會插入一個 Load-Barrier(讀屏障)指令,這個指令會強制讓本地內(nèi)存中的變量值失效,從而重新從主內(nèi)存中讀取最新的值。

        三分惡面渣逆襲:volatile寫插入內(nèi)存屏障后生成的指令序列示意圖

        例如,我們聲明一個 volatile 變量 x:

        volatile int x = 0

        線程 A 對 x 寫入后會將其最新的值刷新到主內(nèi)存中,線程 B 讀取 x 時由于本地內(nèi)存中的 x 失效了,就會從主內(nèi)存中讀取最新的值,內(nèi)存可見性達成!

        三分惡面渣逆襲:volatile內(nèi)存可見性

        HashMap為什么用紅黑樹,鏈表轉數(shù)條件,紅黑樹插入刪除規(guī)則

        三分惡面渣逆襲:JDK 8 HashMap 數(shù)據(jù)結構示意圖

        HashMap 的核心是一個動態(tài)數(shù)組(Node[] table),用于存儲鍵值對。這個數(shù)組的每個元素稱為一個“桶”(Bucket),每個桶的索引是通過對鍵的哈希值進行哈希函數(shù)處理得到的。

        當多個鍵經(jīng)哈希處理后得到相同的索引時,會發(fā)生哈希沖突。HashMap 通過鏈表來解決哈希沖突——即將具有相同索引的鍵值對通過鏈表連接起來。

        不過,鏈表過長時,查詢效率會比較低,于是當鏈表的長度超過 8 時(且數(shù)組的長度大于 64),鏈表就會轉換為紅黑樹。紅黑樹的查詢效率是 O(logn),比鏈表的 O(n) 要快。數(shù)組的查詢效率是 O(1)。

        紅黑樹怎么保持平衡的?

        紅黑樹有兩種方式保持平衡:旋轉染色

        ①、旋轉:旋轉分為兩種,左旋和右旋

        三分惡面渣逆襲:左旋
        三分惡面渣逆襲:右旋

        ②、染?:

        三分惡面渣逆襲:染色

        參考鏈接

        • 三分惡的面渣逆襲:https://javabetter.cn/sidebar/sanfene/nixi.html
        • 二哥的 Java 進階之路:https://javabetter.cn

        ending

        一個人可以走得很快,但一群人才能走得更遠。二哥的編程星球已經(jīng)有 5100 多名球友加入了,如果你也需要一個良好的學習環(huán)境,戳鏈接 ?? 加入我們吧。這是一個編程學習指南 + Java 項目實戰(zhàn) + LeetCode 刷題的私密圈子,你可以閱讀星球專欄、向二哥提問、幫你制定學習計劃、和球友一起打卡成長。

        兩個置頂帖「球友必看」和「知識圖譜」里已經(jīng)沉淀了非常多優(yōu)質(zhì)的學習資源,相信能幫助你走的更快、更穩(wěn)、更遠。

        歡迎點擊左下角閱讀原文了解二哥的編程星球,這可能是你學習求職路上最有含金量的一次點擊。

        最后,把二哥的座右銘送給大家:沒有什么使我停留——除了目的,縱然岸旁有玫瑰、有綠蔭、有寧靜的港灣,我是不系之舟。共勉 ??。

        瀏覽 2661
        3點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        3點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            伊人久操网 | 色噜噜国产在线观看 | 国产无码操逼视频 | 亚州Av电影免费看 | 黄色肉肉视频 | 少妇又紧又色又爽又刺激视频网站 | 偷拍自拍成人 | 精品国产露脸对白在线观看 | 91久久精品美女高潮 | 加勒比综合在线 |