欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java8 Lambda和Invokedynamic詳情

 更新時間:2021年09月22日 09:13:21   作者:sofia  
關(guān)于Java8的Lambda 我們可以將lambda表達式與新的Streams API結(jié)合起來,以表達豐富的數(shù)據(jù)處理查詢,下面文章小編就列舉簡單的例子給大家介說吧,感興趣的小伙伴可以參考下面文章的具體內(nèi)容奧

一、闡明lambda

Java8于2014年3月發(fā)布,并引入了lambda表達式作為其旗艦功能。我們可能已經(jīng)在代碼庫中使用它們來編寫更簡潔、更靈活的代碼。例如,我們可以將lambda表達式與新的Streams API結(jié)合起來,以表達豐富的數(shù)據(jù)處理查詢:

int total = invoices.stream()
                    .filter(inv -> inv.getMonth() == Month.JULY)
                    .mapToInt(Invoice::getAmount)
                    .sum();

此示例顯示如何從發(fā)票集合中計算7月份到期的總金額。傳遞lambda表達式以查找月份為7月的發(fā)票,并傳遞方法引用以從發(fā)票中提取金額。

您可能想知道Java編譯器如何在幕后實現(xiàn)lambda表達式和方法引用,以及Java虛擬機(JVM)如何處理它們。例如,lambda表達式只是匿名內(nèi)部類的語法糖嗎?畢竟,可以通過將lambda表達式的主體復(fù)制到匿名類的相應(yīng)方法的主體中來翻譯上面的代碼

int total = invoices.stream()
                    .filter(new Predicate<Invoice>() {
                        @Override
                        public boolean test(Invoice inv) {
                            return inv.getMonth() == Month.JULY;
                        }
                    })
                    .mapToInt(new ToIntFunction<Invoice>() {
                        @Override
                        public int applyAsInt(Invoice inv) {
                            return inv.getAmount();
                        }
                    })
                    .sum();

本文將解釋為什么Java編譯器不遵循這種機制,并將闡明lambda表達式和方法引用是如何實現(xiàn)的。我們將研究字節(jié)碼生成,并在實驗室中簡要分析lambda性能。最后,我們將討論現(xiàn)實世界中的性能影響。

二、匿名內(nèi)部類

匿名內(nèi)部類具有可能影響應(yīng)用程序性能的不良特征。

首先,編譯器為每個匿名內(nèi)部類生成一個新的類文件。文件名通??雌饋硐?code>ClassName$1,其中ClassName是定義匿名內(nèi)部類的類的名稱,后跟一個美元符號和一個數(shù)字。生成許多類文件是不可取的,因為每個類文件在使用之前都需要加載和驗證,這會影響應(yīng)用程序的啟動性能。加載可能是一項昂貴的操作,包括磁盤I/O和解壓縮JAR文件本身。

如果將lambda轉(zhuǎn)換為匿名內(nèi)部類,則每個lambda都會有一個新的類文件。由于每個匿名內(nèi)部類都將被加載,因此它將占用JVM元空間的空間(這是永久生成的Java8替代品)。如果JVM將每個匿名內(nèi)部類中的代碼編譯成機器代碼,那么它將存儲在代碼緩存中。此外,這些匿名內(nèi)部類將被實例化為單獨的對象。因此,匿名內(nèi)部類會增加應(yīng)用程序的內(nèi)存消耗。引入緩存機制以減少所有這些內(nèi)存開銷可能會有所幫助,這促使引入某種抽象層。

最重要的是,從第一天起選擇使用匿名內(nèi)部類實現(xiàn)lambda將限制未來lambda實現(xiàn)更改的范圍,以及它們根據(jù)未來JVM改進而發(fā)展的能力。

讓我們看一下以下代碼:

import java.util.function.Function;
public class AnonymousClassExample {
    Function<String, String> format = new Function<String, String>() {
        public String apply(String input){
            return Character.toUpperCase(input.charAt(0)) + input.substring(1);
        }
    };
}

我們可以使用命令檢查為任何類文件生成的字節(jié)碼

javap -c -v ClassName 

為作為匿名內(nèi)部類創(chuàng)建的函數(shù)生成的相應(yīng)字節(jié)碼如下所示:

0: aload_0       
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0       
5: new           #2 // class AnonymousClassExample$1
8: dup           
9: aload_0       
10: invokespecial #3 // Method AnonymousClass$1."<init>":(LAnonymousClassExample;)V
13: putfield      #4 // Field format:Ljava/util/function/Function;
16: return  

此代碼顯示以下內(nèi)容:

  • 5:使用字節(jié)碼操作new實例化匿名類示例$1類型的對象。同時在堆棧上推送對新創(chuàng)建對象的引用。
  • 8:dup操作在堆棧上復(fù)制該引用。
  • 10:然后,該值由invokespecial指令使用,該指令初始化匿名內(nèi)部類實例。
  • 13:堆棧頂部現(xiàn)在仍然包含對對象的引用,該引用使用putfield指令存儲在AnonymousClassExample類的format字段中。

AnonymousClassExample$1是編譯器為匿名內(nèi)部類生成的名稱。如果您想讓自己放心,還可以檢查AnonymousClassExample$1類文件,您將找到函數(shù)接口實現(xiàn)的代碼。

lambda表達式轉(zhuǎn)換為匿名內(nèi)部類將限制未來可能的優(yōu)化(例如緩存),因為它們與匿名內(nèi)部類字節(jié)碼生成機制相關(guān)聯(lián)。因此,語言和JVM工程師需要一個穩(wěn)定的二進制表示,該表示提供了足夠的信息,同時允許JVM在將來使用其他可能的實現(xiàn)策略。下一節(jié)將解釋這是如何實現(xiàn)的!

三、Lambdas和Invokedynamic

為了解決上一節(jié)中解釋的問題,Java語言和JVM工程師決定將轉(zhuǎn)換策略的選擇推遲到運行時。Java7引入的新invokedynamic字節(jié)碼指令為他們提供了一種高效實現(xiàn)這一點的機制。lambda表達式到字節(jié)碼的轉(zhuǎn)換分兩步執(zhí)行:

  1. 生成一個invokedynamic調(diào)用站點(稱為lambda工廠),調(diào)用該站點時,該站點返回lambda正在轉(zhuǎn)換到的功能接口的實例;
  2. lambda表達式體轉(zhuǎn)換為將通過invokedynamic指令調(diào)用的方法。

為了說明第一步,讓我們檢查編譯包含lambda表達式的簡單類時生成的字節(jié)碼,例如:

import java.util.function.Function;

public class Lambda {
    Function<String, Integer> f = s -> Integer.parseInt(s);
}

這將轉(zhuǎn)換為以下字節(jié)碼:

0: aload_0
 1: invokespecial #1 // Method java/lang/Object."<init>":()V
 4: aload_0
 5: invokedynamic #2, 0 // InvokeDynamic
                  #0:apply:()Ljava/util/function/Function;
10: putfield #3 // Field f:Ljava/util/function/Function;
13: return

請注意,方法引用的編譯方式略有不同,因為javac不需要生成合成方法,可以直接引用該方法。

第二步的執(zhí)行方式取決于lambda表達式是非捕獲(lambda不訪問在其主體外部定義的任何變量)還是捕獲(lambda訪問在其主體外部定義的變量)。

非捕獲lambda被簡單地分解為一個靜態(tài)方法,該方法具有與lambda表達式完全相同的簽名,并在使用lambda表達式的同一類中聲明。例如,可以將上面lambda類中聲明的lambda表達式分解為如下方法:

static Integer lambda$1(String s) {
    return Integer.parseInt(s);
}

注意:$1不是一個內(nèi)部類,它只是我們表示編譯器生成代碼的方式

捕獲lambda表達式的情況稍微復(fù)雜一些,因為捕獲的變量必須與lambda的形式參數(shù)一起傳遞給實現(xiàn)lambda表達式主體的方法。在這種情況下,常見的轉(zhuǎn)換策略是在lambda表達式的參數(shù)前面加上每個捕獲變量的附加參數(shù)。讓我們看一個實際的例子:

int offset = 100;
Function<String, Integer> f = s -> Integer.parseInt(s) + offset; 


相應(yīng)的方法實現(xiàn)可以通過asy生成:

static Integer lambda$1(int offset, String s) {
    return Integer.parseInt(s) + offset;
}

然而,這種轉(zhuǎn)換策略并不是一成不變的,因為invokedynamic指令的使用使編譯器能夠靈活地在將來選擇不同的實現(xiàn)策略。例如,捕獲的值可以裝箱到數(shù)組中,或者,如果lambda表達式讀取使用它的類的某些字段,則生成的方法可以是實例方法,而不是聲明為靜態(tài)的,從而避免將這些字段作為附加參數(shù)傳遞。

四、性能表現(xiàn)

這種方法的主要優(yōu)點是性能特性。如果把它們看作是可以簡化為一個數(shù)字,那就太好了,但實際上這里涉及到多個操作。

第一步是聯(lián)動步驟,與上述lambda工廠步驟相對應(yīng)。如果我們將性能與匿名內(nèi)部類進行比較,那么等效的操作將是匿名內(nèi)部類的類加載。Oracle已經(jīng)發(fā)布了Sergey Kuksenko對這一權(quán)衡的性能分析,您可以看到Kuksenko在2013年JVM語言峰會上就這一主題發(fā)表了演講[3]。分析表明,需要時間來預(yù)熱lambda工廠方法,在此過程中,初始速度較慢。當有足夠多的調(diào)用站點鏈接時,如果代碼位于熱路徑上(即調(diào)用頻率足以編譯JIT的路徑),則性能與類加載一致。另一方面,如果是冷路徑,lambda工廠方法可以快100倍。

第二步是從周圍范圍捕獲變量。正如我們已經(jīng)提到的,如果沒有要捕獲的變量,那么可以自動優(yōu)化此步驟,以避免使用基于lambda工廠的實現(xiàn)分配新對象。在匿名內(nèi)部類方法中,我們將實例化一個新對象。為了優(yōu)化等效情況,您必須通過創(chuàng)建單個對象并將其提升到靜態(tài)字段來手動優(yōu)化代碼。例如:

// Hoisted Function
public static final Function<String, Integer> parseInt = new Function<String, Integer>() {
    public Integer apply(String arg) {
        return Integer.parseInt(arg);
    }
}; 

// Usage:
int result = parseInt.apply(“123”);

第三步是調(diào)用實際方法。目前,匿名內(nèi)部類和lambda表達式都執(zhí)行完全相同的操作,因此這里的性能沒有差異。非捕獲lambda表達式的開箱即用性能已經(jīng)領(lǐng)先于提升的匿名內(nèi)部類。捕獲lambda表達式的實現(xiàn)與分配匿名內(nèi)部類以捕獲這些字段的性能類似。

我們在本節(jié)中看到,lambda表達式的實現(xiàn)大體上表現(xiàn)良好。雖然匿名內(nèi)部類需要手動優(yōu)化以避免分配,但JVM已經(jīng)為我們優(yōu)化了最常見的情況(一個不捕獲其參數(shù)的lambda表達式)。

當然,理解整體性能模型是很好的,但是在實踐中,事情是如何疊加的呢?我們已經(jīng)在一些軟件項目中使用了Java8,并取得了積極的成果。自動優(yōu)化非捕獲lambda可以提供很好的好處。這里有一個特別的例子,它提出了一些關(guān)于未來優(yōu)化方向的有趣問題。

所討論的示例發(fā)生在處理某些代碼以供系統(tǒng)使用時,該系統(tǒng)需要特別低的GC暫停,理想情況下沒有。因此,希望避免分配太多的對象。該項目廣泛使用lambdas來實現(xiàn)回調(diào)處理程序。不幸的是,我們?nèi)匀挥邢喈敹嗟幕卣{(diào),其中我們沒有捕獲局部變量,但希望引用當前類的字段,甚至只調(diào)用當前類上的方法。目前,這似乎仍然需要分配。下面是一個代碼示例,旨在闡明我們所討論的內(nèi)容:

public MessageProcessor() {} 

public int processMessages() {
    return queue.read(obj -> {
        if (obj instanceof NewClient) {
            this.processNewClient((NewClient) obj);
        } 
        ...
    });
}

這個問題有一個簡單的解決辦法。我們將代碼提升到構(gòu)造函數(shù)中,并將其分配給一個字段,然后在調(diào)用站點直接引用該字段。下面是我們之前重寫的代碼示例:

private final Consumer<Msg> handler; 

public MessageProcessor() {
    handler = obj -> {
        if (obj instanceof NewClient) {
            this.processNewClient((NewClient) obj);
        }
        ...
    };
} 

public int processMessages() {
    return queue.read(handler);
}

在所討論的項目中,這是一個嚴重的問題:內(nèi)存分析顯示,此模式負責前八個對象分配站點中的六個,以及應(yīng)用程序總分配的60%以上。

與任何潛在的優(yōu)化一樣,無論環(huán)境如何,應(yīng)用這種方法都可能會帶來其他問題。

您選擇編寫非慣用代碼純粹是出于性能原因。因此有一個可讀性權(quán)衡

這也關(guān)系到分配的權(quán)衡。您正在向MessageProcessor添加一個字段,使其更大,以便分配。相關(guān)lambda的創(chuàng)建和捕獲也會減慢對MessageProcessor的構(gòu)造函數(shù)調(diào)用。

我們不是通過尋找場景,而是通過內(nèi)存分析發(fā)現(xiàn)了這種情況,并且有一個很好的業(yè)務(wù)用例證明了優(yōu)化的合理性。我們還處于這樣一個位置:對象只分配一次,大量重用lambda表達式,因此緩存非常有益。與任何性能調(diào)整練習(xí)一樣,通常推薦使用科學(xué)方法。

這也是任何其他最終用戶尋求優(yōu)化其lambda表達式使用的方法。嘗試編寫干凈、簡單且功能強大的代碼始終是最好的第一步。任何優(yōu)化,如本次吊裝,應(yīng)僅針對真正的問題進行。編寫捕獲分配對象的lambda表達式本身并不壞——正如編寫調(diào)用'new Foo()'的Java代碼本身也不壞一樣。

這一經(jīng)驗也確實表明,要充分利用lambda表達式,重要的是要習(xí)慣地使用它們。如果lambda表達式用于表示小的純函數(shù),則它們幾乎不需要從其周圍范圍捕獲任何內(nèi)容。和大多數(shù)事情一樣,如果你保持簡單,事情就會表現(xiàn)得很好。

結(jié)論
在本文中,我們解釋了lambda不僅僅是隱藏的匿名內(nèi)部類,以及為什么匿名內(nèi)部類不是lambda表達式的合適實現(xiàn)方法。通過lambda表達式實現(xiàn)方法,已經(jīng)進行了大量的工作。目前,對于大多數(shù)任務(wù),它們都比匿名內(nèi)部類快,但當前的狀態(tài)并不完美;測量驅(qū)動的手動優(yōu)化仍有一定的空間。

Java8中使用的方法不僅僅局限于Java本身。Scala歷來通過生成匿名內(nèi)部類來實現(xiàn)其lambda表達式。在Scala2.12中,我們已經(jīng)開始使用Java8中引入的lambda元工廠機制。隨著時間的推移,JVM上的其他語言也可能采用這種機制。

到此這篇關(guān)于Java8 LambdaInvokedynamic詳情的文章就介紹到這了,更多相關(guān)Java8 LambdaInvokedynamic內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Springboot中Instant時間傳參及序列化詳解

    Springboot中Instant時間傳參及序列化詳解

    這篇文章主要介紹了Springboot中Instant時間傳參及序列化詳解,Instant是Java8引入的一個精度極高的時間類型,可以精確到納秒,但實際使用的時候不需要這么高的精確度,通常到毫秒就可以了,需要的朋友可以參考下
    2023-11-11
  • java設(shè)計模式:建造者模式之生產(chǎn)線

    java設(shè)計模式:建造者模式之生產(chǎn)線

    這篇文章主要介紹了Java設(shè)計模式之建造者模式,結(jié)合具體實例形式分析了建造者模式的概念、原理、實現(xiàn)方法與相關(guān)使用注意事項,需要的朋友可以參考下
    2021-08-08
  • springboot框架中如何整合mybatis框架思路詳解

    springboot框架中如何整合mybatis框架思路詳解

    這篇文章主要介紹了springboot框架中如何整合mybatis框架,本文通過示例圖文相結(jié)合給大家介紹的非常詳細,需要的朋友可以參考下
    2022-12-12
  • 一篇文章帶你搞定JAVA內(nèi)存泄漏

    一篇文章帶你搞定JAVA內(nèi)存泄漏

    今天小編就為大家分享一篇關(guān)于Java內(nèi)存泄漏問題處理方法經(jīng)驗總結(jié),小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2021-07-07
  • SpringMVC結(jié)合ajaxfileupload.js實現(xiàn)文件無刷新上傳

    SpringMVC結(jié)合ajaxfileupload.js實現(xiàn)文件無刷新上傳

    這篇文章主要介紹了SpringMVC結(jié)合ajaxfileupload.js實現(xiàn)文件無刷新上傳,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • 如何解決線程太多導(dǎo)致java socket連接池出現(xiàn)的問題

    如何解決線程太多導(dǎo)致java socket連接池出現(xiàn)的問題

    這篇文章主要介紹了如何解決線程太多導(dǎo)致socket連接池出現(xiàn)的問題,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-12-12
  • struts2實現(xiàn)多文件上傳

    struts2實現(xiàn)多文件上傳

    這篇文章主要為大家詳細介紹了struts2實現(xiàn)多文件上傳,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • Java中如何將符號分隔的文本文件txt轉(zhuǎn)換為excel

    Java中如何將符號分隔的文本文件txt轉(zhuǎn)換為excel

    這篇文章主要介紹了Java中如何將符號分隔的文本文件txt轉(zhuǎn)換為excel,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-09-09
  • SpringBoot整合HikariCP數(shù)據(jù)庫連接池方式

    SpringBoot整合HikariCP數(shù)據(jù)庫連接池方式

    這篇文章主要介紹了SpringBoot整合HikariCP數(shù)據(jù)庫連接池方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • SpringBoot自動裝配Condition的實現(xiàn)方式

    SpringBoot自動裝配Condition的實現(xiàn)方式

    這篇文章主要介紹了SpringBoot自動裝配Condition的實現(xiàn)方式,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-08-08

最新評論