日韩高清亚洲日韩精品一区二区三区,成熟人妻av无码专区,国产又A又黄又潮娇喘视频,男女猛烈无遮挡免费视频在线观看

Java 和低延遲(java延時(shí)任務(wù))

可以構(gòu)建在對外部事件的響應(yīng)時(shí)間方面滿足非常嚴(yán)格的要求的 Java 應(yīng)用程序,但這確實(shí)需要仔細(xì)考慮。本文討論了在 Java 中開發(fā)低延遲代碼時(shí)需要考慮的事項(xiàng)。

每日分享最新,最流行的軟件開發(fā)知識與最新行業(yè)趨勢,希望大家能夠一鍵三連,多多支持,跪求關(guān)注,點(diǎn)贊,留言。

Java 和低延遲(java延時(shí)任務(wù))

我已經(jīng)記不清有多少次我被告知 Java 不是一種適合用于開發(fā)以性能為主要考慮因素的應(yīng)用程序的語言。我的第一反應(yīng)通常是要求澄清“性能”作為兩個(gè)最常見的衡量標(biāo)準(zhǔn)的實(shí)際含義 – 吞吐量和延遲,有時(shí)會(huì)相互沖突,并且針對其中一個(gè)進(jìn)行優(yōu)化的方法可能會(huì)對另一個(gè)產(chǎn)生不利影響.

存在用于開發(fā) Java 應(yīng)用程序的技術(shù),這些技術(shù)可以匹配甚至超過使用更傳統(tǒng)的用于此目的的語言構(gòu)建的應(yīng)用程序的性能要求。但是,從延遲的角度來看,即使這樣也可能不足以獲得最佳性能。Java 應(yīng)用程序仍然必須依賴操作系統(tǒng)來提供對底層硬件的訪問。通常,延遲敏感(通常稱為“實(shí)時(shí)”)應(yīng)用程序在幾乎可以直接訪問底層硬件時(shí)運(yùn)行得最好,這同樣適用于 Java。在本文中,我們將介紹一些當(dāng)我們希望我們的應(yīng)用程序最有效地利用系統(tǒng)資源時(shí)可以采取的方法。

Java 從一開始就被設(shè)計(jì)為在廣泛的硬件和系統(tǒng)架構(gòu)中以二進(jìn)制級別可移植。這是通過設(shè)計(jì)和實(shí)現(xiàn)虛擬機(jī)(執(zhí)行平臺(tái)的抽象模型)并讓它執(zhí)行Java 源編譯器的輸出來完成的。爭論的焦點(diǎn)是遷移到不同類型的硬件平臺(tái)只需要移植虛擬機(jī)。應(yīng)用程序和庫無需修改即可運(yùn)行(“一次編寫,到處運(yùn)行”的口號)。

但是,具有嚴(yán)格延遲和性能要求的應(yīng)用程序通常需要在執(zhí)行時(shí)盡可能接近硬件 – 他們希望從硬件中榨取所有可能的性能,并且不想要純粹為了可移植性或像動(dòng)態(tài)內(nèi)存管理這樣的抽象編程概念阻礙了。

多年來,Java 虛擬機(jī)已經(jīng)發(fā)展成為一個(gè)極其復(fù)雜的執(zhí)行平臺(tái),可以在運(yùn)行時(shí)從 Java 字節(jié)碼生成機(jī)器代碼,并根據(jù)動(dòng)態(tài)收集的指標(biāo)優(yōu)化該代碼。這是靜態(tài)編譯語言(如 C )無法做到的,因?yàn)樗鼈儧]有所需的運(yùn)行時(shí)信息。選擇數(shù)據(jù)結(jié)構(gòu)和算法時(shí)的謹(jǐn)慎方法可以最大限度地減少甚至消除垃圾收集的需要——這可能是 Java 運(yùn)行時(shí)環(huán)境中最明顯的一個(gè)方面,它阻止了一致的延遲時(shí)間。

但歸根結(jié)底,Java 虛擬機(jī)只是——虛擬的——它需要在操作系統(tǒng)之上運(yùn)行以管理其對硬件平臺(tái)的訪問。無論該操作系統(tǒng)是 Linux(可能是服務(wù)器端環(huán)境中使用最廣泛的)、Windows 還是其他操作系統(tǒng),問題仍然存在。

Linux 的“問題”

Linux作為 Unix 操作系統(tǒng)家族的一員,多年來一直在發(fā)展。Unix 的第一個(gè)版本是在 1960 年代后期開發(fā)的。它首先在學(xué)術(shù)界和研究界發(fā)展壯大并獲得了極大的知名度,然后在商業(yè)界以各種形式出現(xiàn)。Linux 已成為 Unix 的主要變體——盡管它仍然保留了許多原始特性。如今,隨著基于容器的執(zhí)行環(huán)境和云的出現(xiàn),它的主導(dǎo)地位幾乎已經(jīng)完全。

但是,從實(shí)時(shí)或延遲敏感型應(yīng)用程序的角度來看,Linux/Unix 確實(shí)存在問題。這些主要源于 Unix 被設(shè)計(jì)為分時(shí)系統(tǒng)這一基本事實(shí)。它最初的硬件平臺(tái)是微型計(jì)算機(jī),同時(shí)被許多不同的用戶共享。所有用戶都有自己的工作要做,而 Unix 竭盡全力確保所有人都能“公平地分享”計(jì)算機(jī)資源。

實(shí)際上,操作系統(tǒng)會(huì)偏愛執(zhí)行大量 I/O 的用戶——包括在終端與系統(tǒng)交互——以犧牲主要執(zhí)行計(jì)算的任務(wù)(所謂的 CPU 密集型作業(yè))為代價(jià)。當(dāng)我們考慮到當(dāng)時(shí)的計(jì)算機(jī)幾乎都是單 CPU(單核)時(shí),這是有道理的。

然而,隨著多 CPU 計(jì)算機(jī)的發(fā)展,需要對 Unix 操作系統(tǒng)的核心進(jìn)行一些認(rèn)真的重新設(shè)計(jì),以允許有效地使用這些執(zhí)行內(nèi)核。但同樣的方法仍然適用,交互式任務(wù)總是比 CPU 密集型任務(wù)更受歡迎。有了多個(gè)內(nèi)核可用,最終效果仍然是提高整體性能。

如今,幾乎每臺(tái)計(jì)算機(jī)都將擁有多個(gè)內(nèi)核,從手機(jī)等移動(dòng)設(shè)備到工作站,再到服務(wù)器級機(jī)器。檢查這些環(huán)境并查看我們是否可以采取不同的方法來改進(jìn)平臺(tái)以更有效地支持實(shí)時(shí)、延遲敏感的應(yīng)用程序似乎是有效的。

我們?nèi)绾谓鉀Q這些問題?

在我工作的 Chronicle Software,我們已經(jīng)開發(fā)了許多開源庫來支持構(gòu)建針對低延遲進(jìn)行優(yōu)化的應(yīng)用程序,這是基于該領(lǐng)域多年的經(jīng)驗(yàn)。本文的其余部分描述了我們學(xué)到的一些幫助我們實(shí)現(xiàn)這一目標(biāo)的東西。

Java 運(yùn)行時(shí)

影響 Java 應(yīng)用程序延遲的主要問題是那些與垃圾收集堆管理和使用鎖同步訪問共享資源有關(guān)的問題。存在解決這兩個(gè)問題的技術(shù),盡管它們確實(shí)需要開發(fā)人員在一定程度上偏離慣用的 Java 編程風(fēng)格。理想情況下,我們會(huì)使用封裝較低級別細(xì)節(jié)和專門技術(shù)的庫,但我們確實(shí)需要了解“幕后”正在發(fā)生的事情。

為低延遲應(yīng)用程序設(shè)計(jì)的框架和庫青睞的一種方法是繞過 Java 垃圾收集器,利用不屬于正常 Java 堆的內(nèi)存(稱為“堆外”內(nèi)存)。內(nèi)存使用正常的操作系統(tǒng)機(jī)制映射到持久存儲(chǔ),或者通過網(wǎng)絡(luò)連接復(fù)制到其他系統(tǒng)。

使用這種方法的明顯優(yōu)勢是對內(nèi)存的訪問不受垃圾收集器的非確定性干預(yù)。缺點(diǎn)是管理在這些區(qū)域中創(chuàng)建的對象的生命周期成為應(yīng)用程序或庫的責(zé)任。

現(xiàn)代應(yīng)用程序的通用架構(gòu)在組件之間包含某種形式的通信,通常基于消息傳遞。消息在通信過程中被序列化為 JSONYAML 等標(biāo)準(zhǔn)格式或從標(biāo)準(zhǔn)格式反序列化,提供此功能的庫通??梢砸敫呒墑e的對象分配。經(jīng)過仔細(xì)考慮,可以選擇經(jīng)過精心設(shè)計(jì)的庫,以最大限度地減少新 Java 對象的創(chuàng)建,從而對性能產(chǎn)生積極影響。

從 Java 的早期開始,對共享可變數(shù)據(jù)的并發(fā)訪問就使用互斥鎖進(jìn)行同步。如果一個(gè)線程試圖獲取另一個(gè)線程持有的鎖,那么它會(huì)被阻塞,直到鎖被釋放。在多核環(huán)境中,可以使用不需要獲取線程阻塞的替代技術(shù)來實(shí)現(xiàn)同步,并且已經(jīng)表明,在大多數(shù)情況下,這對減少延遲有積極的影響。

編寫此類代碼并不簡單,但是,可以在標(biāo)準(zhǔn) Java 庫中的 Lock 接口后面進(jìn)行封裝,甚至可以進(jìn)一步定義允許通過標(biāo)準(zhǔn) API 進(jìn)行安全、無鎖并發(fā)訪問的數(shù)據(jù)結(jié)構(gòu)。一些標(biāo)準(zhǔn)的 Java Collections 庫使用這種方法,盡管這對用戶是透明的。

Linux

公平地說,多年來,Unix 的“實(shí)時(shí)”變體已經(jīng)為專門的應(yīng)用程序提供了不同的執(zhí)行環(huán)境。雖然這些通常是利基產(chǎn)品,但現(xiàn)在許多這些方法和功能在 Unix 和 Linux 的主流發(fā)行版中都可用。

最小化延遲的特性通常分為兩類,內(nèi)存管理和線程調(diào)度。

Linux 進(jìn)程中的所有內(nèi)存,包括 Java 的垃圾收集堆,都會(huì)被臨時(shí)“換出”到磁盤,以便其他進(jìn)程可以在需要將內(nèi)存重新帶入之前將 RAM 用于自己的目的。這一切都會(huì)發(fā)生對進(jìn)程完全透明,內(nèi)存中的數(shù)據(jù)和后備存儲(chǔ)中的數(shù)據(jù)之間的訪問時(shí)間差異可能有幾個(gè)數(shù)量級。當(dāng)然,堆外內(nèi)存也有同樣的行為。

但是,現(xiàn)代 Unix 和 Linux 系統(tǒng)允許標(biāo)記內(nèi)存區(qū)域,以便操作系統(tǒng)在尋找要從進(jìn)程中回收的區(qū)域時(shí)忽略它們。這意味著,對于該進(jìn)程中的那些內(nèi)存區(qū)域,內(nèi)存訪問時(shí)間將是一致的(并且總體上被認(rèn)為更快)。不得不說,在繁忙的Java應(yīng)用程序中,訪問進(jìn)程內(nèi)存的頻率會(huì)降低該內(nèi)存被分頁的可能性,但風(fēng)險(xiǎn)仍然存在。

以這種方式固定一個(gè)進(jìn)程的內(nèi)存意味著其他進(jìn)程的內(nèi)存更少,這可能會(huì)因此受到影響,但在“實(shí)時(shí)”世界中,我們必須有點(diǎn)自私!

為低延遲而設(shè)計(jì)的數(shù)據(jù)結(jié)構(gòu)通常會(huì)默認(rèn)或通過選項(xiàng)提供將其內(nèi)存鎖定或固定在 RAM 中的能力。

Java 程序中的線程,就像來自其他應(yīng)用程序甚至操作系統(tǒng)任務(wù)的線程一樣,可以訪問由稱為調(diào)度程序的操作系統(tǒng)組件管理的 CPU。調(diào)度程序有一組策略,用于決定選擇哪些需要訪問 CPU 的線程(稱為 Runnable 線程)——通常 Runnable 線程比 CPU 多。

如前所述,Unix/Linux 中的傳統(tǒng)調(diào)度策略旨在支持交互式線程而不是 CPU 綁定線程。如果我們試圖運(yùn)行對延遲敏感的應(yīng)用程序,這對我們沒有幫助——我們希望我們的線程以某種方式優(yōu)先于其他非延遲敏感的線程。

現(xiàn)代 Unix/Linux 系統(tǒng)提供了可以提供這些功能的替代調(diào)度策略,通過允許將線程調(diào)度優(yōu)先級固定在高級別,以便它們在可運(yùn)行時(shí)總是從其他線程接管 CPU 資源,這意味著它們可以更多地響應(yīng)事件迅速地。

但也可以進(jìn)一步影響調(diào)度程序的行為。通常,在管理線程時(shí)會(huì)使用所有可用的 CPU 資源。如今,可以更改調(diào)度程序使用哪些 CPU。我們可以從調(diào)度程序可用的 CPU 中完全移除 CPU,并將它們專門用于我們的專用線程。

或者,我們可以將 CPU 分成組,并將一組 CPU 與特定的線程組相關(guān)聯(lián)。此功能是 Linux 更通用的資源管理組件(稱為組)的一部分。它構(gòu)成了 Linux 對虛擬化的支持的一部分,并且是實(shí)現(xiàn)容器的關(guān)鍵,例如在現(xiàn)代環(huán)境中由 Docker 生成的容器。但是,它可以通過特定的系統(tǒng)調(diào)用用于一般應(yīng)用程序。

就像上面描述的內(nèi)存鎖定一樣,我們是自私的,因?yàn)檫@樣做顯然會(huì)對系統(tǒng)的其他部分產(chǎn)生負(fù)面影響。需要非常小心地配置以獲得最佳結(jié)果,因?yàn)殄e(cuò)誤的可能性很高,而且出錯(cuò)的后果可能很嚴(yán)重。

結(jié)論

編寫和部署低延遲應(yīng)用程序是一項(xiàng)高技能活動(dòng),不僅需要了解所使用的語言,還需要了解應(yīng)用程序運(yùn)行的環(huán)境。在本文中,我概述了一些需要考慮的領(lǐng)域,以及如何解決這些問題。

資源

要詳細(xì)了解本文中討論的一些主題,請查看本書。

相關(guān)新聞

聯(lián)系我們
聯(lián)系我們
公眾號
公眾號
在線咨詢
分享本頁
返回頂部