本篇內容介紹了“Java虛擬機經(jīng)典問題有哪些”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
創(chuàng)新互聯(lián)公司是一家專注于成都網(wǎng)站設計、成都網(wǎng)站制作與策劃設計,化隆網(wǎng)站建設哪家好?創(chuàng)新互聯(lián)公司做網(wǎng)站,專注于網(wǎng)站建設10余年,網(wǎng)設計領域的專業(yè)建站公司;建站業(yè)務涵蓋:化隆等地區(qū)。化隆做網(wǎng)站價格咨詢:028-86922220Java 是如何在保證可移植性的前提下提供高執(zhí)行效率的?
Java 程序最為常見的執(zhí)行方式,是預先編譯為一種名為 Java 字節(jié)碼的中間代碼格式。這種代碼格式無法直接運行在 CPU 之上,而是需要借助 JVM 來執(zhí)行。換句話說,只要某個平臺提供了合乎 JVM 規(guī)范的實現(xiàn),它便能執(zhí)行這份 Java 字節(jié)碼。這也就是我們經(jīng)常說的“一次編寫,到處運行”。
主流的 OpenJDK/OracleJDK 中所提供的 JVM 叫做 HotSpot。它同時采用了解釋執(zhí)行和即時編譯。解釋執(zhí)行就好比同聲傳譯,JVM 一邊理解輸入的字節(jié)碼一邊向 CPU 發(fā)出指令序列;即時編譯則是“磨刀不誤砍柴工”,JVM 會在運行過程中將熱點代碼編譯成為可直接執(zhí)行的二進制代碼。
這種混合執(zhí)行模式是建立在程序符合二八定律的假設上,即百分之二十的代碼占據(jù)了百分之八十的計算資源。對于不常用代碼,我們無需耗費時間將其編譯成二進制代碼,而是采取解釋執(zhí)行的方式運行;另一方面,對于僅占據(jù)小部分的熱點代碼,JVM 則會花費時間將其編譯為二進制代碼,以達到理想的運行效率。
異常捕獲是如何實現(xiàn)的?
在編譯生成的 Java 字節(jié)碼中,每個方法都附帶一個異常表。異常表中的每一行均定義了一條異常執(zhí)行路徑,其中包括規(guī)定捕獲范圍的起始字節(jié)碼索引、終止(不包含)字節(jié)碼索引,異常處理代碼的起始字節(jié)碼索引,以及所捕獲的異常類型。
當程序觸發(fā)異常時,JVM 會從上至下遍歷異常表中的所有條目。當觸發(fā)異常的字節(jié)碼的索引值在某行異常表條目的捕獲范圍內,JVM 會判斷所拋出的異常和該條目想要捕獲的異常是否匹配。如果匹配,JVM 會將控制流轉移至該條目所指向的異常處理代碼。
上述異常捕獲機制還被用于 finally 從句的實現(xiàn)。通常,Java 程序的編譯器 javac 會復制多份 finally 代碼塊,放置于生成的 Java 字節(jié)碼之中,然后通過生成多行異常表條目,來實現(xiàn)完整的 finally 邏輯。
反射調用為什么慢?
默認情況下,反射調用首先會被委派給 native 方法來進行。可想而知,其運行效率低下。當某個反射調用的調用次數(shù)達到 15 之后,JDK 代碼斷定該調用屬于熱點調用。繼而,JDK 將動態(tài)生成直接調用目標方法的字節(jié)碼,并將反射調用的委派對象由原本的 native 方法實現(xiàn)切換至該動態(tài)生成的實現(xiàn)。這種方式的運行效率相對于 native 方法來說要高很多。
之所以 JDK 不從一開始便采用動態(tài)生成字節(jié)碼的方式,主要是因為生成過程需要耗費一定的時間。對于那些整個生命周期中僅執(zhí)行數(shù)次的反射調用,動態(tài)生成字節(jié)碼將得不償失。
然而,即便是直接調用目標方法的動態(tài)實現(xiàn),其峰值性能也無法跟真正的直接調用相媲美。這背后涉及到即時編譯中的虛方法內聯(lián)。
相關文章: < 方法內聯(lián)(下)>
垃圾回收的基礎思想是什么?
目前 JVM 的主流垃圾回收器采取的都是可達性分析算法。該算法的實質是將一系列被稱為 GC Roots 的對象作為初始的存活對象合集,然后從該合集出發(fā)探索所有能夠被該集合引用到的對象,并標記為存活對象。當標記階段結束之后,未被標記到的對象便是可以清除的。
傳統(tǒng)的垃圾回收算法在標記、清除過程中需要中止其他應用線程,即所謂的 Stop-The-World。新型的垃圾回收算法,如 CMS、G1 以及 ZGC,盡可能地實現(xiàn)并發(fā)標記、清除,從而讓 Stop-The-World 的時間長度可控。
垃圾回收的另一基礎思想則是分代回收。JVM 會將新生成的對象劃為新生代,而將在多次垃圾回收中存活下來的對象劃為老年代。JVM 會為不同的分代設置不同的回收算法,從而達到新生代多收集、快收集,老年代少收集、全收集的目標。
如何理解 Java 內存模型?
現(xiàn)代計算機多為對稱多處理器的體系架構。每個處理器均有獨立的寄存器組和緩存(這在 Java 內存模型中被抽象為工作內存);多個處理器可同時執(zhí)行同一進程中的不同線程。
在 Java 程序中,不同線程可能訪問同一變量或對象。如果任由編譯器或處理器對這些訪問進行優(yōu)化,則很可能出現(xiàn)在單線程執(zhí)行思維下無法想象的問題。因此,Java 語言規(guī)范引入了 Java 內存模型,通過定義多項規(guī)則對編譯器和處理器進行限制。
這些規(guī)則所體現(xiàn)的最為重要的屬性便是可見性,即對某一變量的訪問能否被同一線程的其他操作,或者不同線程所觀測到。Java 內存模型引入了多種 happens-before 關系,以實現(xiàn)上述可見性。以 volatile 字段為例,對其的寫操作 happens before 這之后的讀操作,也就是說,我們總能讀到 volatile 字段的最新值。
JVM 如何應對對象鎖的各種場景?
重量級鎖是最為基礎、最為低效的對象鎖實現(xiàn)。JVM 會阻塞加鎖失敗的線程,并且在目標鎖被釋放的時候,喚醒這些線程。我們用等紅燈作類比。Java 線程進入阻塞狀態(tài)相當于熄火停車,再次點火啟動必然耗費時間。JVM 會在進入阻塞狀態(tài)之前進行自旋,也就是怠速停車。如果目標鎖能夠在短時間內被釋放出來,該線程便能夠不進入阻塞狀態(tài),直接獲取該鎖。
重量級鎖針對的是多個線程同時競爭同一把鎖的場景。在現(xiàn)實中,多個線程可能在不同時間段持有同一把鎖。為了應對這種沒有鎖競爭的情況,JVM 采用了輕量級鎖機制。在加鎖時,JVM 將在鎖對象處做標記,指向當前線程的棧上;在解鎖時,上述標記會被清除。如果某線程在請求鎖時,發(fā)現(xiàn)該鎖為輕量級鎖,并且指向另一線程所對應的棧,那么它會將該鎖膨脹為重量級鎖。
偏向鎖所應對的場景則更為樂觀:至始至終只有一個線程請求某把鎖。JVM 采取的做法是在第一次加鎖時為鎖對象做標記,使其指向當前線程的地址;在解鎖時則不做任何操作。如果下一次請求該鎖的仍是同一線程,便直接跳過標記過程;否則,JVM 會將該鎖膨脹為輕量級鎖。
“Java虛擬機經(jīng)典問題有哪些”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關的知識可以關注創(chuàng)新互聯(lián)-成都網(wǎng)站建設公司網(wǎng)站,小編將為大家輸出更多高質量的實用文章!
標題名稱:Java虛擬機經(jīng)典問題有哪些-創(chuàng)新互聯(lián)
分享鏈接:http://m.newbst.com/article14/jgcge.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供云服務器、網(wǎng)站改版、App開發(fā)、企業(yè)網(wǎng)站制作、虛擬主機、微信小程序
聲明:本網(wǎng)站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)