N4V28資訊網——每日最新資訊28at.com
重點介紹一個你覺得有意義的項目?
回答技巧和思路:N4V28資訊網——每日最新資訊28at.com
- 介紹的項目業務難度和技術難點要高一些,最好是微服務項目。
- 簡明扼要的講清楚項目核心板塊的業務場景即可,切忌不要講的太細和太久,這只是面試官要考察你技術問題的一個觸手。
- 講清楚自己在項目中負責的模塊。
- 講清楚項目的亮點是啥。
- 講清楚遇到了哪些棘手的問題?以及最終的解決方案。
synchronize鎖升級過程?
synchronized 鎖升級有兩個版本:N4V28資訊網——每日最新資訊28at.com
- JDK 1.6 synchronized 鎖升級:無鎖 -> 偏向鎖 -> 輕量級鎖 -> 重量級鎖。
- JDK 15 synchronized 鎖升級:無鎖 -> 輕量級鎖 -> 重量級鎖。
注意:回答的過程中,最好能講明白每種鎖的概念和特征,以及為什么 JDK 15 要廢除偏向鎖?N4V28資訊網——每日最新資訊28at.com
synchronize作用在普通方法和靜態方法上有什么區別?
synchronized 作用在靜態方法是類級別的,而作用在普通方法是對象實例級別,因此作用在靜態方法時,鎖范圍更大、性能也更低。N4V28資訊網——每日最新資訊28at.com
synchronized實現原理?
synchronized 同步鎖是通過 JVM 內置的 Monitor 監視器實現的,而監視器又是依賴操作系統的互斥鎖 Mutex 實現的。N4V28資訊網——每日最新資訊28at.com
例如,以下代碼中使用了 synchronized 修飾:N4V28資訊網——每日最新資訊28at.com
public class SynchronizedToMonitorExample { public static void main(String[] args) { int count = 0; synchronized (SynchronizedToMonitorExample.class) { for (int i = 0; i < 10; i++) { count++; } } System.out.println(count); }}
當我們將上述代碼編譯成字節碼之后,得到的結果是這樣的:N4V28資訊網——每日最新資訊28at.com
N4V28資訊網——每日最新資訊28at.com
從上述結果我們可以看出,在 main 方法中多了一對 monitorenter 和 monitorexit 的指令,它們的含義是:N4V28資訊網——每日最新資訊28at.com
- monitorenter:表示進入監視器。
- monitorexit:表示退出監視器。
由此可知 synchronized 是依賴 Monitor 監視器實現的。N4V28資訊網——每日最新資訊28at.com
volatile作用和實現原理?
volatile 作用有以下兩個:N4V28資訊網——每日最新資訊28at.com
- 保證內存可見性;
- 保證有序性(禁止指令重排序)。
① 內存可見性實現原理
volatile 內存可見性主要通過 lock 前綴指令實現的,它會鎖定當前內存區域的緩存(緩存行),并且立即將當前緩存行數據寫入主內存(耗時非常短),回寫主內存的時候會通過 MESI 協議使其他線程緩存了該變量的地址失效,從而導致其他線程需要重新去主內存中重新讀取數據到其工作線程中。N4V28資訊網——每日最新資訊28at.com
什么 MESI 協議?
MESI 協議,全稱為 Modified, Exclusive, Shared, Invalid,是一種高速緩存一致性協議。它是為了解決多處理器(CPU)在并發環境下,多個 CPU 緩存不一致問題而提出的。MESI 協議定義了高速緩存中數據的四種狀態:N4V28資訊網——每日最新資訊28at.com
- Modified(M):表示緩存行已經被修改,但還沒有被寫回主存儲器。在這種狀態下,只有一個 CPU 能獨占這個修改狀態。
- Exclusive(E):表示緩存行與主存儲器相同,并且是主存儲器的唯一拷貝。這種狀態下,只有一個 CPU 能獨占這個狀態。
- Shared(S):表示此高速緩存行可能存儲在計算機的其他高速緩存中,并且與主存儲器匹配。在這種狀態下,各個 CPU 可以并發的對這個數據進行讀取,但都不能進行寫操作。
- Invalid(I):表示此緩存行無效或已過期,不能使用。
MESI 協議的主要用途是確保在多個 CPU 共享內存時,各個 CPU 的緩存數據能夠保持一致性。當某個 CPU 對共享數據進行修改時,它會將這個數據的狀態從 S(共享)或 E(獨占)狀態轉變為 M(修改)狀態,并等待適當的時機將這個修改寫回主存儲器。同時,它會向其他 CPU 廣播一個“無效消息”,使得其他 CPU 將自己緩存中對應的數據狀態轉變為I(無效)狀態,從而在下次訪問這個數據時能夠從主存儲器或其他 CPU 的緩存中重新獲取正確的數據。N4V28資訊網——每日最新資訊28at.com
這種協議可以確保在多處理器環境中,各個 CPU 的緩存數據能夠正確、一致地反映主存儲器中的數據狀態,從而避免由于緩存不一致導致的數據錯誤或程序異常。N4V28資訊網——每日最新資訊28at.com
② 有序性實現原理
volatile 的有序性是通過插入內存屏障(Memory Barrier),在內存屏障前后禁止重排序優化,以此實現有序性的。N4V28資訊網——每日最新資訊28at.com
什么是內存屏障?
內存屏障(Memory Barrier 或 Memory Fence)是一種硬件級別的同步操作,它強制處理器按照特定順序執行內存訪問操作,確保內存操作的順序性,阻止編譯器和 CPU 對內存操作進行不必要的重排序。內存屏障可以確保跨越屏障的讀寫操作不會交叉進行,以此維持程序的內存一致性模型。N4V28資訊網——每日最新資訊28at.com
在 Java 內存模型(JMM)中,volatile 關鍵字用于修飾變量時,能夠保證該變量的可見性和有序性。關于有序性,volatile 通過內存屏障的插入來實現:N4V28資訊網——每日最新資訊28at.com
- 寫內存屏障(Store Barrier / Write Barrier):當線程寫入 volatile 變量時,JMM 會在寫操作前插入 StoreStore 屏障,確保在這次寫操作之前的所有普通寫操作都已完成。接著在寫操作后插入 StoreLoad 屏障,強制所有后來的讀寫操作都在此次寫操作完成之后執行,這就確保了其他線程能立即看到 volatile 變量的最新值。
- 讀內存屏障(Load Barrier / Read Barrier):當線程讀取 volatile 變量時,JMM 會在讀操作前插入 LoadLoad 屏障,確保在此次讀操作之前的所有讀操作都已完成。而在讀操作后插入 LoadStore 屏障,防止在此次讀操作之后的寫操作被重排序到讀操作之前,這樣就確保了對 volatile 變量的讀取總是能看到之前對同一變量或其他相關變量的寫入結果。
通過這種方式,volatile 關鍵字有效地實現了內存操作的順序性,從而保證了多線程環境下對 volatile 變量的操作遵循 happens-before 原則,確保了并發編程的正確性。N4V28資訊網——每日最新資訊28at.com
JVM內存布局?
《Java虛擬機規范》中將 JVM 運行時數據區域劃分為以下 5 部分:N4V28資訊網——每日最新資訊28at.com
- 程序計數器(Program Counter Register):用于存儲當前線程執行的字節碼指令的地址,在多線程環境中,程序計數器用于實現線程切換,保證線程恢復執行時能夠繼續從正確的位置執行代碼。
- Java 虛擬機棧(Java Virtual Machine Stacks):用于存儲方法調用和局部變量(方法內部定義的變量),在方法調用和返回時,虛擬機棧用于保存方法的調用幀,包括方法的局部變量、操作數棧、方法返回地址等。
- 本地方法棧(Native Method Stack):與虛擬機棧類似,本地方法棧用于執行本地(Native)方法。
- Java 堆(Java Heap):JVM 中最大的一塊內存區域,用于存儲對象實例。所有的對象都在堆中分配內存。
- 方法區(Methed Area):用于存儲類的元數據信息,包括類的結構、字段、方法、靜態變量、常量池等。
如下圖所示:N4V28資訊網——每日最新資訊28at.com
N4V28資訊網——每日最新資訊28at.com
棧和方法區會有OOM嗎?
棧和方法區都會出現 OOM,它們的 OOM 發生場景如下:N4V28資訊網——每日最新資訊28at.com
- 棧內存主要用于存儲方法的棧幀,包括局部變量、操作數棧、方法返回地址等。當一個線程請求的棧深度超過 JVM 允許的最大深度時(默認情況下這個值是比較大的,但可以通過-Xss參數調整),會拋出 StackOverflowError 異常。此外,如果 JVM 嘗試動態擴展棧空間大小但無法獲得足夠的內存,也可能拋出 OutOfMemoryError 異常。不過,這種情況相對較少見,因為棧空間一般在啟動時就已經固定或者有比較確定的上限。
- 當方法區(或元空間)中存放的類、常量、靜態變量等信息超過了 JVM 分配給它的內存大小時(可通過 -XX:MaxMetaspaceSize 等參數進行設置),就會拋出 OutOfMemoryError。特別是當應用動態加載大量的類或類加載器無法被垃圾回收時,容易出現方法區的內存溢出。
父線程創建多個子線程可能會導致哪塊內存溢出?
可能會導致內存溢出的區域有以下幾個:N4V28資訊網——每日最新資訊28at.com
- Java 棧內存溢出:每個線程都有自己的棧,用于存儲方法調用時的方法信息、局部變量等數據。如果線程的棧設置過大,或者線程遞歸深度過深,可能會導致棧內存溢出(StackOverflowError)。若棧大小動態擴展受限于系統可用內存,則可能拋出 OutOfMemoryError。
- 堆內存溢出:當創建大量線程時,每個線程可能會創建和管理多個對象,這些對象都存儲在堆中,當對象超過 JVM 配置的最大堆內存時(通過 -Xmx 參數設置),可能會導致 java.lang.OutOfMemoryError: Java heap space 錯誤。
- 方法區溢出: 在多線程應用中,當線程中的代碼涉及到動態類加載(例如使用線程上下文類加載器加載不同的類)時,可能會導致方法區(或其替代品 Metaspace)內存的快速增長。方法區存儲了類的元數據,如果這部分內存達到 JVM 配置的最大值(通過 -XX:MaxMetaspaceSize 設置),也會引發 java.lang.OutOfMemoryError,錯誤信息可能涉及“Metaspace”。
CMS和G1收集器的區別?
CMS 和 G1 區別主要有以下幾個:N4V28資訊網——每日最新資訊28at.com
- 目標和定位不同:CMS 目標和定位是最短停頓時間;G1 除了追求低停頓外,還允許用戶設定期望的最大停頓時間(Pause Time Target),更加靈活地平衡吞吐量和響應時間,適合大規模數據的應用。
- 作用區域不同:CMS 是老年代垃圾回收器;G1 是全代(新生代+老年代)垃圾回收器。
- 使用算法不同:CMS 使用的是“標記-清除”算法,所以會產生內存碎片;而 G1 使用的是“復制”算法所以不會有內存碎片。
- 適用場景不同:小內存和 JDK 8 之前通常會使用 CMS 垃圾回收器;而大內存管理和 JDK 9+ 以上環境通常會使用 G1 垃圾回收器。
說說創建一個對象的過程?
創建一個對象的過程如下:N4V28資訊網——每日最新資訊28at.com
- 類加載檢查:當通過 new 關鍵字創建一個對象時,JVM 首先會檢查該對象的類是否已經被加載并初始化了。如果尚未加載,JVM 將先進行類的加載過程,然后調用該類的構造器以完成初始化。
- 分配內存空間:類加載完成后,JVM 會在 Java 堆中為新創建的對象分配足夠的內存空間。對象所需的內存大小在類加載過程中就已經確定。內存的分配方式取決于 Java 堆內存是否規整,可以選擇“指針碰撞”或“空閑列表”兩種不同的分配方式。
- 初始化零值:內存分配之后,JVM 會對對象的普通成員變量進行初始化為零值,比如 int 類型初始化為 0,Integer 類型初始化為 null。這一步驟確保了對象的成員字段在不經過顯式初始化的情況下也能被直接使用。
- 設置對象頭:然后 JVM 需要對對象的對象頭進行設置,這包括對象的元數據信息、GC 分代年齡、 hashCode 以及鎖標記等。對象頭的設置對于對象的后續使用至關重要。
- 執行構造方法:最后,JVM 將執行對象的構造方法。這一步是開發者可以控制的,可以在構造方法中添加自定義的初始化代碼,以實現特定的業務邏輯。構造方法執行完成后,一個完全初始化且可直接使用的對象才算創建成功。
注意:對象創建過程和類加載機制(加載、驗證、準備、解析、初始化)略有不同。N4V28資訊網——每日最新資訊28at.com
本文鏈接:http://www.tebozhan.com/showinfo-26-94437-0.html百度真題和答案解析!
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 4款.NET開源、功能強大的Windows桌面工具箱
下一篇: 打擊侵權,我國海關查獲 20 萬張未申報出口游戲卡片