首頁 Java java教程 如何理解Java中的逃逸

如何理解Java中的逃逸

Apr 28, 2023 pm 07:19 PM
java

在Java的編譯體系中,一個Java的原始碼檔案變成電腦執行的機器指令的過程中,需要經過兩段編譯,第一段是把.java檔轉換成.class檔。第二段編譯是把.class轉換成機器指令的過程。

第一段編譯就是javac指令。

在第二編譯階段,JVM 透過解釋字節碼將其翻譯成對應的機器指令,逐條讀入,逐條解釋翻譯。很顯然,經過解釋執行,其執行速度必然會比可執行的二進位字節碼程式慢很多。這就是傳統的JVM的解譯器(Interpreter)的功能。為了解決這種效率問題,引入了 JIT(即時編譯) 技術。

在引入了 JIT 技術後,Java程式還是透過解釋器進行解釋執行,當JVM發現某個方法或程式碼區塊運作特別頻繁的時候,就會認為這是「熱點程式碼」(Hot Spot Code)。然後JIT會把部分「熱點代碼」翻譯成本地機器相關的機器碼,並進行優化,然後再把翻譯後的機器碼緩存起來,以備下次使用。

由於關於JIT編譯和熱點檢測的內容,我在深入分析Java的編譯原理中已經介紹過了,這裡就不在贅述,本文主要來介紹下JIT中的最佳化。 JIT優化中最重要的一個就是逃逸分析。

逃逸分析

關於逃逸分析的概念,可以參考物件和陣列並不是都在堆上分配記憶體的。一文,這裡簡單回顧一下:

# 逃逸分析的基本行為是分析物件動態作用域:當一個物件在方法中被定義後,它可能被外部方法所引用,例如作為呼叫參數傳遞到其他地方中,稱為方法逃逸。

例如以下程式碼:

public static StringBuffer craeteStringBuffer(String s1, String s2) {

#     StringBuffer sb = new StringBuffer();

    sb.append(s1);

#     sb.append(s2);

#     return sb;

}

public static String createStringBuffer(String s1, String s2) {

#     StringBuffer sb = new StringBuffer();

    sb.append(s1);

#     sb.append(s2);

#     return sb.toString();

# }

第一段程式碼中的sb就逃逸了,而第二段程式碼中的sb就沒有逃逸。

使用逃逸分析,編譯器可以對程式碼做如下最佳化:

一、同步省略。如果一個物件被發現只能從一個執行緒被存取到,那麼對於這個物件的操作可以不考慮同步。

二、將堆分配轉換為棧分配。如果一個物件在子程式中被分配,要使指向該物件的指標永遠不會逃逸,物件可能是堆疊分配的候選,而不是堆疊分配。

三、分離對像或標量替換。有的物件可能不需要作為一個連續的記憶體結構存在也可以被存取到,那麼物件的部分(或全部)可以不儲存在內存,而是儲存在CPU暫存器中。

在Java程式碼執行時,透過JVM參數可指定是否開啟逃逸分析,

# -XX: DoEscapeAnalysis : 表示開啟逃逸分析

-XX:-DoEscapeAnalysis : 表示關閉逃逸分析 從jdk 1.7開始已經預設開始逃逸分析,如需關閉,需要指定-XX:-DoEscapeAnalysis

同步省略

# 在動態編譯同步區塊的時候,JIT編譯器可以藉助逃逸分析來判斷同步區塊所使用的鎖定物件是否只能夠被一個執行緒存取而沒有被發佈到其他執行緒。

如果同步區塊所使用的鎖定物件透過這種分析被證實只能夠被一個執行緒訪問,那麼JIT編譯器在編譯這個同步區塊的時候就會取消對這部分程式碼的同步。這個取消同步的過程就叫同步省略,也叫鎖消除。

如以下程式碼:

public void f() {

    Object hollis = new Object();

#     synchronized(hollis) {

#         System.out.println(hollis);

    }

}

程式碼中對hollis這個物件進行加鎖,但是hollis物件的生命週期只在f()方法中,並不會被其他執行緒所存取到,所以在JIT編譯階段就會被最佳化掉。優化成:

public void f() {

    Object hollis = new Object();

#     System.out.println(hollis);

# }

所以,在使用synchronized的時候,如果JIT經過逃逸分析之後發現並無線程安全問題的話,就會做鎖定消除。

標量替換

標量(Scalar)是指一個無法再分解成更小的資料的資料。 Java中的原始資料型別就是標量。相對的,那些還可以分解的資料叫做聚合量(Aggregate),Java中的物件就是聚合量,因為他可以分解成其他聚合量和標量。

在JIT階段,如果經過逃逸分析,發現一個物件不會被外界訪問的話,那麼經過JIT優化,就會把這個物件拆解成若干個其中包含的若干個成員變數來代替。這個過程就是標量替換。

public static void main(String[] args) {

#    alloc();

# }

private static void alloc() {

   Point point = new Point(1,2);

#    System.out.println("point.x=" point.x "; point.y=" point.y);

}

class Point{

    private int x;

    private int y;

}

以上程式碼中,point物件並沒有逃逸出alloc方法,且point物件是可以拆解成標量的。那麼,JIT就會不會直接建立Point對象,而是直接使用兩個標量int x ,int y來取代Point對象。

以上程式碼,經過標量替換後,就會變成:

private static void alloc() {

   int x = 1;

   int y = 2;

   System.out.println("point.x=" x "; point.y=" y);

}

可以看到,Point這個聚合量經過逃逸分析後,發現他並沒有逃逸,就被替換成兩個聚合量了。那麼標量替換有什麼好處呢?就是可以大大減少堆記憶體的佔用。因為一旦不需要建立物件了,那就不再需要分配堆記憶體了。

標量替換為堆疊上分配提供了很好的基礎。

堆疊上分配

在Java虛擬機器中,物件是在Java堆中分配記憶體的,這是一個普遍的常識。但是,有一種特殊情況,那就是如果經過逃逸分析後發現,一個物件並沒有逃逸出方法的話,那麼就可能被優化成棧上分配。這樣就無需在堆上分配內存,也無須進行垃圾回收了。

關於堆疊上分配的詳細介紹,可以參考物件和陣列並不是都在堆上分配記憶體的。 。

這裡,還是要簡單說一下,其實在現有的虛擬機器中,並沒有真正的實作棧上分配,在物件和陣列並不是都在堆上分配記憶體的。在中我們的例子中,物件沒有在堆上分配,其實是標量替換實現的。

逃逸分析並不成熟

關於逃逸分析的論文在1999年就已經發表了,但直到JDK 1.6才有實現,而且這項技術到如今也並不是十分成熟的。

根本原因就是無法保證逃逸分析的效能消耗一定能高於他的消耗。雖然經過逃逸分析可以做標量替換、棧上分配、和鎖消除。但是逃逸分析本身也是需要進行一連串複雜的分析的,這其實也是一個相對耗時的過程。

一個極端的例子,就是經過逃逸分析之後,發現沒有一個物體是不逃逸的。那這個逃逸分析的過程就白白浪費掉了。

雖然這項技術並不十分成熟,但他也是即時編譯器優化技術中十分重要的手段。

以上是如何理解Java中的逃逸的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
4 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Java 中的完美數 Java 中的完美數 Aug 30, 2024 pm 04:28 PM

Java 完美數指南。這裡我們討論定義,如何在 Java 中檢查完美數?

Java 中的隨機數產生器 Java 中的隨機數產生器 Aug 30, 2024 pm 04:27 PM

Java 隨機數產生器指南。在這裡,我們透過範例討論 Java 中的函數,並透過範例討論兩個不同的生成器。

Java中的Weka Java中的Weka Aug 30, 2024 pm 04:28 PM

Java 版 Weka 指南。這裡我們透過範例討論簡介、如何使用 weka java、平台類型和優點。

Java 中的史密斯數 Java 中的史密斯數 Aug 30, 2024 pm 04:28 PM

Java 史密斯數指南。這裡我們討論定義,如何在Java中檢查史密斯號?帶有程式碼實現的範例。

Java Spring 面試題 Java Spring 面試題 Aug 30, 2024 pm 04:29 PM

在本文中,我們保留了最常被問到的 Java Spring 面試問題及其詳細答案。這樣你就可以順利通過面試。

突破或從Java 8流返回? 突破或從Java 8流返回? Feb 07, 2025 pm 12:09 PM

Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

Java 中的時間戳至今 Java 中的時間戳至今 Aug 30, 2024 pm 04:28 PM

Java 中的時間戳記到日期指南。這裡我們也結合範例討論了介紹以及如何在java中將時間戳記轉換為日期。

創造未來:零基礎的 Java 編程 創造未來:零基礎的 Java 編程 Oct 13, 2024 pm 01:32 PM

Java是熱門程式語言,適合初學者和經驗豐富的開發者學習。本教學從基礎概念出發,逐步深入解說進階主題。安裝Java開發工具包後,可透過建立簡單的「Hello,World!」程式來實踐程式設計。理解程式碼後,使用命令提示字元編譯並執行程序,控制台上將輸出「Hello,World!」。學習Java開啟了程式設計之旅,隨著掌握程度加深,可創建更複雜的應用程式。

See all articles