<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                最后一課時我們來分析常見的 JVM 面試題。 市面上關于 JVM 的面試題實在太多了,本課程中的第 02 ~ 06 課時是理論面試題的重災區,并且是比較深入的題目,而本課時則選取了一些基礎且常見的題目。 有些面試題是開放性的,而有些面試題是知識性的,要注意區別。面試題并沒有標準答案,尤其是開放性題目,你需要整理成白話文,來盡量的展示自己。如果你在回答的過程中描述了一些自己不是很熟悉的內容,可能會受到追問。所以,根據問題,建議整理一份適合自己的答案,這比拿來主義更讓人印象深刻。 #### 勘誤 我們來回憶一下課程中曾講解過的容易出錯或模糊的知識點。 不知你是否還記得?我們在每一課時的講解中,都有聚焦的點,不同的問法可能會有不同的回答,要注意。 #### 對象在哪里分配? 在第 02 課時中,談到了數組和對象是堆上分配,當學完第 22 課時的逃逸分析后,我們了解到并不完全是這樣的。由于 JIT 的存在,如果發現某些對象沒有逃逸出方法,那么就有可能被優化成了棧上分配。 #### CMS 是老年代垃圾回收器? 初步印象是,但實際上不是。根據 CMS 的各個收集過程,它其實是一個涉及年輕代和老年代的綜合性垃圾回收器。在很多文章和書籍的劃分中,都將 CMS 劃分為了老年代垃圾回收器,加上它主要作用于老年代,所以一般誤認為是。 #### 常量池問題 常量池的表述有些模糊,在此細化一下,注意我們指的是 Java 7 版本之后。 JVM 中有多個常量池: * 字符串常量池,存放在堆上,也就是執行 intern 方法后存的地方,class 文件的靜態常量池,如果是字符串,則也會被裝到字符串常量池中。 * 運行時常量池,存放在方法區,屬于元空間,是類加載后的一些存儲區域,大多數是類中 constant_pool 的內容。 * 類文件常量池,也就是 constant_pool,這個是概念性的,并沒有什么實際存儲區域。 在平常的交流過程中,聊的最多的是字符串常量池,具體可參考官網。 #### ZGC 支持的堆上限? Java 13 增加到 16TB,Java 11 還是 4 TB,技術在發展,請保持關注。 年輕代提升閾值動態計算的描述 在第 06 課時中對于年輕代“動態對象年齡判定”的表述是錯誤的。 參考代碼 share/gc/shared/ageTable.cpp 中的 compute_tenuring_threshold 函數,重新表述為:程序從年齡最小的對象開始累加,如果累加的對象大小,大于幸存區的一半,則將當前的對象 age 作為新的閾值,年齡大于此閾值的對象則直接進入老年代。 這里說的一半,是通過 TargetSurvivorRatio 參數進行設置的。 #### 永久代 雖然課程一直在強調,是基于 Java 8+ 版本進行講解的,但還是有讀者提到了永久代。這部分知識容易發生混淆,面試頻率也很高,建議集中消化一下。 ![](https://img.kancloud.cn/01/f4/01f4e89785f534346da5689c848e1d3c_1017x817.png) 上面是第 02 課時中的一張圖,注意左半部分是 Java 8 版本之前的內存區域,右半部分是 Java 8 的內存區域,主要區別就在 Perm 區和 Metaspace 區。 Perm 區屬于堆,獨立控制大小,在 Java 8 中被移除了(JEP122),原來的方法區就在這里;Metaspace 是非堆,默認空間無上限,方法區移動到了這里。 #### 常見面試題 * [ ] JVM 有哪些內存區域?(JVM 的內存布局是什么?) JVM 包含堆、元空間、Java 虛擬機棧、本地方法棧、程序計數器等內存區域,其中,堆是占用內存最大的一塊,如下圖所示。 ![](https://img.kancloud.cn/e9/e1/e9e1bcfce48aa30f337d291f3dc87e64_757x410.jpg) * [ ] Java 的內存模型是什么?(JMM 是什么?) JVM 試圖定義一種統一的內存模型,能將各種底層硬件以及操作系統的內存訪問差異進行封裝,使 Java 程序在不同硬件以及操作系統上都能達到相同的并發效果。它分為工作內存和主內存,線程無法對主存儲器直接進行操作,如果一個線程要和另外一個線程通信,那么只能通過主存進行交換,如下圖所示。 ![](https://img.kancloud.cn/59/f2/59f2a8c8bf44b32329a5a9994688521a_1004x662.png) * [ ] JVM 垃圾回收時如何確定垃圾?什么是 GC Roots? JVM 采用的是可達性分析算法。JVM 是通過 GC Roots 來判定對象存活的,從 GC Roots 向下追溯、搜索,會產生一個叫做 Reference Chain 的鏈條。當一個對象不能和任何一個 GC Root 產生關系時,就判定為垃圾,如下圖所示。 ![](https://img.kancloud.cn/a9/75/a97506fd80e7e6a76296704770cd4d68_1415x759.png) GC Roots 大體包括: * 活動線程相關的各種引用,比如虛擬機棧中 棧幀里的引用; * 類的靜態變量引用; * JNI 引用等。 注意:要想回答的更詳細一些,請參照第 05 課時中的內容。 * [ ] 能夠找到 Reference Chain 的對象,就一定會存活么? 不一定,還要看 Reference 類型,弱引用在 GC 時會被回收,軟引用在內存不足的時候會被回收,但如果沒有 Reference Chain 對象時,就一定會被回收。 * [ ] 強引用、軟引用、弱引用、虛引用是什么? * 普通的對象引用關系就是強引用。 * 軟引用用于維護一些可有可無的對象。只有在內存不足時,系統則會回收軟引用對象,如果回收了軟引用對象之后仍然沒有足夠的內存,才會拋出內存溢出異常。 * 弱引用對象相比軟引用來說,要更加無用一些,它擁有更短的生命周期,當 JVM 進行垃圾回收時,無論內存是否充足,都會回收被弱引用關聯的對象。 * 虛引用是一種形同虛設的引用,在現實場景中用的不是很多,它主要用來跟蹤對象被垃圾回收的活動。 * [ ] 你說你做過 JVM 參數調優和參數配置,請問如何查看 JVM 系統默認值 使用 -XX:+PrintFlagsFinal 參數可以看到參數的默認值,這個默認值還和垃圾回收器有關,比如 UseAdaptiveSizePolicy。 * [ ] 你平時工作中用過的 JVM 常用基本配置參數有哪些? 主要有 Xmx、Xms、Xmn、MetaspaceSize 等。 更加詳細的可參照第 23 課時的參數總結,你只需要記憶 10 個左右即可,建議記憶 G1 相關的參數。面試時間有限,不會在這上面糾結,除非你表現的太囂張了。 * [ ] 請你談談對 OOM 的認識 OOM 是非常嚴重的問題,除了程序計數器,其他內存區域都有溢出的風險。和我們平常工作最密切的,就是堆溢出,另外,元空間在加載的類非常多的情況下也會溢出,還有就是棧溢出,這個通常影響比較小。堆外也有溢出的可能,這個就比較難排查了。 * [ ] 你都有哪些手段用來排查內存溢出? 這個話題很大,可以從實踐環節中隨便摘一個進行總結,下面舉一個最普通的例子。 內存溢出包含很多種情況,我在平常工作中遇到最多的就是堆溢出。有一次線上遇到故障,重新啟動后,使用 jstat 命令,發現 Old 區一直在增長。我使用 jmap 命令,導出了一份線上堆棧,然后使用 MAT 進行分析,通過對 GC Roots 的分析,發現了一個非常大的 HashMap 對象,這個原本是其他同事做緩存用的,但是一個無界緩存,造成了堆內存占用一直上升,后來,將這個緩存改成 guava 的 Cache,并設置了弱引用,故障就消失了。 * [ ] GC 垃圾回收算法與垃圾收集器的關系? 常用的垃圾回收算法有標記清除、標記整理、復制算法等,引用計數器也算是一種,但垃圾回收器不使用這種算法,因為有循環依賴的問題。 ![](https://img.kancloud.cn/2d/6d/2d6ddb76a4242cd997aa0661e18c6068_757x461.jpg) 很多垃圾回收器都是分代回收的: * 對于年輕代,主要有 Serial、ParNew 等垃圾回收器,回收過程主要使用復制算法; * 老年代的回收算法有 Serial、CMS 等,主要使用標記清除、標記整理算法等。 我們線上使用較多的是 G1,也有年輕代和老年代的概念,不過它是一個整堆回收器,它的回收對象是小堆區 。 * [ ] 生產上如何配置垃圾收集器? ![](https://img.kancloud.cn/e5/ca/e5cab69791ec0322c9767b5664dbf40e_1197x275.png) 首先是內存大小問題,基本上每一個內存區域我都會設置一個上限,來避免溢出問題,比如元空間。通常,堆空間我會設置成操作系統的 2/3,超過 8GB 的堆,優先選用 G1。 然后我會對 JVM 進行初步優化,比如根據老年代的對象提升速度,來調整年輕代和老年代之間的比例。 接下來是專項優化,判斷的主要依據是系統容量、訪問延遲、吞吐量等,我們的服務是高并發的,所以對 STW 的時間非常敏感。 我會通過記錄詳細的 GC 日志,來找到這個瓶頸點,借用 GCeasy 這樣的日志分析工具,很容易定位到問題。 * [ ] 怎么查看服務器默認的垃圾回收器是哪一個? 這通常會使用另外一個參數,即 -XX:+PrintCommandLineFlags,來打印所有的參數,包括使用的垃圾回收器。 * [ ] 假如生產環境 CPU 占用過高,請談談你的分析思路和定位。 ![](https://img.kancloud.cn/f2/99/f299309e1fbee7d2729c07b5643a9a61_757x316.jpg) 首先,使用 top -H 命令獲取占用 CPU 最高的線程,并將它轉化為十六進制。 然后,使用 jstack 命令獲取應用的棧信息,搜索這個十六進制,這樣就能夠方便地找到引起 CPU 占用過高的具體原因。 * [ ] 對于 JDK 自帶的監控和性能分析工具用過哪些? * jps:用來顯示 Java 進程; * jstat:用來查看 GC; * jmap:用來 dump 堆; * jstack:用來 dump 棧; * jhsdb:用來查看執行中的內存信息。 * [ ] 棧幀都有哪些數據? 棧幀包含:局部變量表、操作數棧、動態連接、返回地址等。 ![](https://img.kancloud.cn/e5/f4/e5f4d933a03c82e93fd0aeb22cba38d9_757x439.jpg) ? * [ ] JIT 是什么? 為了提高熱點代碼的執行效率,在運行時,虛擬機將會把這些代碼編譯成與本地平臺相關的機器碼,并進行各種層次的優化,完成這個任務的編譯器,就稱為即時編譯器(Just In Time Compiler),簡稱 JIT 編譯器。 * [ ] Java 的雙親委托機制是什么? 雙親委托的意思是,除了頂層的啟動類加載器以外,其余的類加載器,在加載之前,都會委派給它的父加載器進行加載,這樣一層層向上傳遞,直到祖先們都無法勝任,它才會真正的加載,Java 默認是這種行為。 ![](https://img.kancloud.cn/9a/29/9a29f74771d2b94addde4c8c58f2a356_632x315.png) * [ ] 有哪些打破了雙親委托機制的案例? * Tomcat 可以加載自己目錄下的 class 文件,并不會傳遞給父類的加載器; * Java 的 SPI,發起者是 BootstrapClassLoader,BootstrapClassLoader 已經是最上層了,它直接獲取了 AppClassLoader 進行驅動加載,和雙親委派是相反的。 * [ ] 簡單描述一下(分代)垃圾回收的過程 ![](https://img.kancloud.cn/b7/ad/b7ad2a1e95f340f36e8bafba27d9a537_757x277.jpg) 分代回收器有兩個分區:老生代和新生代,新生代默認的空間占總空間的 1/3,老生代的默認占比是 2/3。 新生代使用的是復制算法,新生代里有 3 個分區:Eden、To Survivor、From Survivor,它們的默認占比是 8:1:1。 當年輕代中的 Eden 區分配滿的時候,就會觸發年輕代的 GC(Minor GC),具體過程如下: * 在 Eden 區執行了第一次 GC 之后,存活的對象會被移動到其中一個 Survivor 分區(以下簡稱 from); * Eden 區再次 GC,這時會采用復制算法,將 Eden 和 from 區一起清理,存活的對象會被復制到 to 區,接下來,只要清空 from 區就可以了。 * [ ] CMS 分為哪幾個階段? * 初始標記 * 并發標記 * 并發預清理 * 并發可取消的預清理 * 重新標記 * 并發清理 由于《深入理解 Java 虛擬機》一書的流行,面試時省略**并發清理、并發可取消的預清理**這兩個階段,一般也是沒問題的。 * [ ] CMS 都有哪些問題? * 內存碎片問題,Full GC 的整理階段,會造成較長時間的停頓; * 需要預留空間,用來分配收集階段產生的“浮動垃圾”; * 使用更多的 CPU 資源,在應用運行的同時進行堆掃描; * 停頓時間是不可預期的。 * [ ] 你使用過 G1 垃圾回收器的哪幾個重要參數? ![](https://img.kancloud.cn/fc/d9/fcd9080d39ecf722affe0fe79d0a301c_757x338.jpg) 最重要的是 MaxGCPauseMillis,可以通過它設定 G1 的目標停頓時間,它會盡量去達成這個目標。G1HeapRegionSize 可以設置小堆區的大小,一般是 2 的次冪。InitiatingHeapOccupancyPercent 啟動并發 GC 時的堆內存占用百分比,G1 用它來觸發并發 GC 周期,基于整個堆的使用率,而不只是某一代內存的使用比例,默認是 45%。 * [ ] GC 日志的 real、user、sys 是什么意思? * real 指的是從開始到結束所花費的時間,比如進程在等待 I/O 完成,這個阻塞時間也會被計算在內。 * user 指的是進程在用戶態(User Mode)所花費的時間,只統計本進程所使用的時間,是指多核。 * sys ?指的是進程在核心態(Kernel Mode)所花費的 CPU 時間量,即內核中的系統調用所花費的時間,只統計本進程所使用的時間。 * [ ] 什么情況會造成元空間溢出? 元空間默認是沒有上限的,不加限制比較危險。當應用中的 Java 類過多時,比如 Spring 等一些使用動態代理的框架生成了很多類,如果占用空間超出了我們的設定值,就會發生元空間溢出。 * [ ] 什么時候會造成堆外內存溢出? 使用了 Unsafe 類申請內存,或者使用了 JNI 對內存進行操作,這部分內存是不受 JVM 控制的,不加限制使用的話,會很容易發生內存溢出。 * [ ] SWAP 會影響性能么? 當操作系統內存不足時,會將部分數據寫入到 SWAP ,但是 SWAP 的性能是比較低的。如果應用的訪問量較大,需要頻繁申請和銷毀內存,那么很容易發生卡頓。一般在高并發場景下,會禁用 SWAP。 * [ ] 有什么堆外內存的排查思路? 進程占用的內存,可以使用 top 命令,看 RES 段占用的值,如果這個值大大超出我們設定的最大堆內存,則證明堆外內存占用了很大的區域。 使用 gdb 命令可以將物理內存 dump 下來,通常能看到里面的內容。更加復雜的分析可以使用 Perf 工具,或者谷歌開源的 GPerftools。那些申請內存最多的 native 函數,就很容易找到。 * [ ] HashMap 中的 key,可以是普通對象么?有什么需要注意的地方? Map 的 key 和 value 可以是任何類型,但要注意的是,一定要重寫它的 equals 和 hashCode 方法,否則容易發生內存泄漏。 * [ ] 怎么看死鎖的線程? 通過 jstack 命令,可以獲得線程的棧信息,死鎖信息會在非常明顯的位置(一般是最后)進行提示。 * [ ] 如何寫一段簡單的死鎖代碼? 詳情請見第 15 課時的 DeadLockDemo,筆試的話頻率也很高。 * [ ] invokedynamic 指令是干什么的? invokedynamic 是 Java 7 版本之后新加入的字節碼指令,使用它可以實現一些動態類型語言的功能。我們使用的 Lambda 表達式,在字節碼上就是 invokedynamic 指令實現的,它的功能有點類似反射,但它是使用方法句柄實現的,執行效率更高。 * [ ] volatile 關鍵字的原理是什么?有什么作用? 使用了 volatile 關鍵字的變量,每當變量的值有變動的時候,都會將更改立即同步到主內存中;而如果某個線程想要使用這個變量,就先要從主存中刷新到工作內存,這樣就確保了變量的可見性。 一般使用一個 volatile 修飾的 bool 變量,來控制線程的運行狀態。 ``` volatile?boolean?stop?=?false; void?stop(){ this.stop?=?true; } void?start(){ new?Thread(()->{ while?(!stop){ //sth } }).start(); } ``` * [ ] 什么是方法內聯? 為了減少方法調用的開銷,可以把一些短小的方法,比如 getter/setter,納入到目標方法的調用范圍之內,這樣就少了一次方法調用,速度就能得到提升,這就是方法內聯的概念。 * [ ] 對象是怎么從年輕代進入老年代的? 在下面 4 種情況下,對象會從年輕代進入到老年代。 * 如果對象夠老,則會通過提升(Promotion)的方式進入老年代,一般根據對象的年齡進行判斷。 * 動態對象年齡判定,有的垃圾回收算法,比如 G1,并不要求 age 必須達到 15 才能晉升到老年代,它會使用一些動態的計算方法。 * 分配擔保,當 Survivor 空間不夠的時候,則需要依賴其他內存(指老年代)進行分配擔保,這個時候,對象也會直接在老年代上分配。 * 超出某個大小的對象將直接在老年代上分配,不過這個值默認為 0,意思是全部首選 Eden 區進行分配。 ![](https://img.kancloud.cn/45/32/4532b0d62f1013d7acef92fafcf6860c_757x621.jpg) * [ ] safepoint 是什么? 當發生 GC 時,用戶線程必須全部停下來,才可以進行垃圾回收,這個狀態我們可以認為 JVM 是安全的(safe),整個堆的狀態是穩定的。 ![](https://img.kancloud.cn/e4/7d/e47d6e88b66a7c63d79946a15913969e_757x350.jpg) 如果在 GC 前,有線程遲遲進入不了 safepoint,那么整個 JVM 都在等待這個阻塞的線程,造成了整體 GC 的時間變長。 * [ ] MinorGC、MajorGC、FullGC 都什么時候發生? MinorGC 在年輕代空間不足的時候發生,MajorGC 指的是老年代的 GC,出現 MajorGC 一般經常伴有 MinorGC。 FullGC 有三種情況:第一,當老年代無法再分配內存的時候;第二,元空間不足的時候;第三,顯示調用 System.gc 的時候。另外,像 CMS 一類的垃圾回收器,在 MinorGC 出現 promotion failure 的時候也會發生 FullGC。 * [ ] 類加載有幾個過程? 加載、驗證、準備、解析、初始化。 ![](https://img.kancloud.cn/f0/75/f075a2f0bc0d2e0a6266347a83b2646d_530x456.png) * [ ] 什么情況下會發生棧溢出? 棧的大小可以通過 -Xss 參數進行設置,當遞歸層次太深的時候,則會發生棧溢出。 * [ ] 生產環境服務器變慢,請談談診斷思路和性能評估? 希望第 11 課時和第 16 課時中的一些思路,能夠祝你一臂之力。下圖是第 11 課時的一張影響因素的全景圖。 ![](https://img.kancloud.cn/a7/6e/a76e8b620e21c768dc24d0c9a0f093ae_757x457.jpg) 從各個層次分析代碼優化的手段,如下圖所示: ![](https://img.kancloud.cn/69/51/6951d7dab2bb350592b8fc9e89867762_1273x768.png) 如果你應聘的是比較高級的職位,那么可以說一下第 23 課時中的最后總結部分。 #### 小結 本課時我們首先修正了一些表述錯誤的知識點;然后分析了一些常見的面試題,這些面試題的覆蓋率是非常有限的,因為很多細節都沒有觸及到,更多的面試題還需要你自行提取、整理,由于篇幅有限,這里不再重復。 到現在為止,我們的課程內容就結束了。本課程的特色主要體現在實踐方面,全部都是工作中的總結和思考;輔之以理論,給你一個在工作中,JVM 相關知識點的全貌。當然,有些課時的難度是比較高的,需要你真正的實際操作一下。 寫作的過程中難免會有遺漏的知識點,可通過拉勾客服人員加入本課程的讀者群一起討論;如果你覺得課程不錯,從中有所收獲的話,不要忘了推薦給身邊的朋友哦。前路漫漫,一起加油。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看