Kotlin內(nèi)存陷阱inline使用技巧示例詳解
引言
inline ,翻譯過來為 內(nèi)聯(lián) ,在 Kotlin 中,一般建議用于 高階函數(shù) 中,目的是用來彌補(bǔ)其運(yùn)行時(shí)的 額外開銷。
其原理也比較簡單,在調(diào)用時(shí)將我們的代碼移動(dòng)到調(diào)用處使用,從而降低方法調(diào)用時(shí)的 棧幀 層級(jí)。
棧幀: 指的是虛擬機(jī)在進(jìn)行方法調(diào)用和方法執(zhí)行時(shí)的數(shù)據(jù)結(jié)構(gòu),每一個(gè)棧幀里都包含了相應(yīng)的數(shù)據(jù),比如 局部參數(shù),操作數(shù)棧等等。
Jvm在執(zhí)行方法時(shí),每執(zhí)行一個(gè)方法會(huì)產(chǎn)生一個(gè)棧幀,隨后將其保存到我們當(dāng)前線程所對(duì)應(yīng)的棧里,方法執(zhí)行完畢時(shí)再將此方法出棧,
所以內(nèi)聯(lián)后就相當(dāng)于省了一個(gè)棧幀調(diào)用。
如果上述描述中,你只記住了后半句,降低棧幀 ,那么此時(shí)你可能已經(jīng)陷入了一個(gè)使用陷阱?
錯(cuò)誤示例
如下截圖中所示,我們隨便創(chuàng)建了一個(gè)方法,并增加了 inline 關(guān)鍵字:

觀察截圖會(huì)發(fā)現(xiàn),此時(shí)IDE已經(jīng)給出了提示,它建議你移除 inline , Why? 為什么呢???
不是說內(nèi)聯(lián)可以提高性能嗎,那么不應(yīng)該任何方法都應(yīng)該加 inline 提高性能嗎?(就是這么倔強(qiáng)????)
上面我們提到了,內(nèi)聯(lián)是會(huì)將代碼移動(dòng)到調(diào)用處,降低 一層棧幀,但這個(gè)性能提升真的大嗎?
再仔細(xì)想想,移動(dòng)到調(diào)用處,移動(dòng)到調(diào)用處。這是什么概念呢?
假設(shè)我們某個(gè)方法里代碼只有兩行(我想不會(huì)有人會(huì)某個(gè)方法只有一行吧??),這個(gè)方法又被好幾處調(diào)用,內(nèi)聯(lián)是提高了調(diào)用性能,畢竟節(jié)省了一次棧幀,再加上方法行數(shù)少(暫時(shí)拋棄虛擬機(jī)優(yōu)化這個(gè)底層條件)。
但如果方法里代碼有幾十行?每次調(diào)用都會(huì)把代碼內(nèi)聯(lián)過來,那調(diào)用處豈不??,帶來的包大小影響某種程度上要比內(nèi)聯(lián)成本更高????!
如下圖所示,我們對(duì)上述示例做一個(gè)論證:

Jvm: 我謝謝你。
推薦示例
我們?cè)谖恼伦铋_始提到了,Kotlin inline ,一般建議用于 高階函數(shù)(lambda) 中。為什么呢?
如下示例:

轉(zhuǎn)成字節(jié)碼后,可以發(fā)現(xiàn),tryKtx() 被創(chuàng)建為了一個(gè)匿名內(nèi)部類 (Simple$test|1) 。每次調(diào)用時(shí),相當(dāng)于需要?jiǎng)?chuàng)建匿名類的實(shí)例對(duì)象,從而導(dǎo)致二次調(diào)用的性能損耗。
那如果我們給其增加 inline 呢???,反編譯后相應(yīng)的 java代碼 如下:

具體對(duì)比圖如上所示,不難發(fā)現(xiàn),我們的調(diào)用處已經(jīng)被替換為原方法,相應(yīng)的 lambda 也被消除了,從而顯著減少了性能損耗。
小結(jié)
如果查看官方庫相應(yīng)的代碼,如下所示,比如 with :
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
不難發(fā)現(xiàn),inline 的大多數(shù)場景僅且在 高階函數(shù) 并且 方法行數(shù)較短 時(shí)適用。因?yàn)閷?duì)于普通方法,jvm本身對(duì)其就會(huì)進(jìn)行優(yōu)化,所以 inline 在普通方法上的的意義幾乎聊勝于無。
總結(jié)
- 因?yàn)閮?nèi)聯(lián)函數(shù)會(huì)將方法函數(shù)移動(dòng)到調(diào)用處,會(huì)增加調(diào)用處的代碼量,所以對(duì)于較長的方法應(yīng)該避免使用;
- 內(nèi)聯(lián)函數(shù)應(yīng)該用于使用了 高階函數(shù)(lambda) 的方法,而不是普通方法。
以上就是Kotlin內(nèi)存陷阱inline使用技巧示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Kotlin內(nèi)存陷阱inline使用的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
logback-spring.xml的配置及示例詳解(直接復(fù)制粘貼可用)
在使用logback作為日志框架時(shí),可以創(chuàng)建一個(gè)名為logback-spring.xml的配置文件來自定義日志輸出的格式和方式,下面這篇文章主要給大家介紹了關(guān)于logback-spring.xml的配置及示例詳解的相關(guān)資料,文中的代碼直接復(fù)制粘貼可用,需要的朋友可以參考下2024-01-01
SpringMVC數(shù)據(jù)輸出相關(guān)知識(shí)總結(jié)
今天帶大家學(xué)習(xí)SpringMVC的相關(guān)知識(shí),文中對(duì)SpringMVC數(shù)據(jù)輸出作了非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)的小伙伴們很有幫助,需要的朋友可以參考下2021-06-06
RabbitMQ中Confirm消息確認(rèn)機(jī)制保障生產(chǎn)端消息的可靠性詳解
這篇文章主要介紹了RabbitMQ中Confirm消息確認(rèn)機(jī)制保障生產(chǎn)端消息的可靠性詳解,生產(chǎn)者將數(shù)據(jù)發(fā)送到 RabbitMQ 的時(shí)候,可能數(shù)據(jù)就在半路給搞丟了,因?yàn)榫W(wǎng)絡(luò)問題啥的,都有可能,需要的朋友可以參考下2023-12-12
Java+Selenium實(shí)現(xiàn)控制瀏覽器的啟動(dòng)選項(xiàng)Options
這篇文章主要為大家詳細(xì)介紹了如何使用java代碼利用selenium控制瀏覽器的啟動(dòng)選項(xiàng)Options的代碼操作,文中的示例代碼講解詳細(xì),感興趣的可以了解一下2023-01-01
Spring Boot 項(xiàng)目啟動(dòng)失敗的解決方案
這篇文章主要介紹了Spring Boot 項(xiàng)目啟動(dòng)失敗的解決方案,幫助大家更好的理解和學(xué)習(xí)使用Spring Boot,感興趣的朋友可以了解下2021-03-03

