2016-07-07 123 views
10

我的問題:等待線程資源消耗

是否大量存在於JVM線程消耗了大量的資源(內存,CPU),當線程TIMED_WAIT狀態(不睡覺)>的99.9% ?當線程在等待時,如果有需要的話,維護它們需要多少CPU開銷?

答案也適用於非JVM相關環境(如Linux內核)嗎?

語境:

我的程序接收到大量的空間消費套餐。它存儲不同軟件包中相似屬性的計數。在收到包裹後的一段時間(可能是幾小時或幾天)之後,該特定包裹將到期,並且該包裹所貢獻的計數應該減少。

目前,我通過將所有包存儲在內存或磁盤中來實現這些功能。每隔5分鐘,我從存儲中刪除過期的軟件包,並掃描剩餘的軟件包以計算屬性。此方法耗盡了大量內存,並且時間複雜度較低(時間和內存的O(n),其中n是未到期軟件包的數量)。這使得該程序的可擴展性非常糟糕。

解決此問題的一種替代方法是在每次出現軟件包時遞增屬性計數,並啓動在軟件包到期後遞減屬性計數的線程Timer()。這消除了存儲所有大型軟件包的需要,並將時間複雜度降低到O(1)。但是,這會產生另一個問題,因爲我的程序將開始具有O(n)線程數,這可能會削減性能。由於大多數線程將處於TIMED_WAIT狀態(Java的Timer()調用Object.wait(long)方法),它們的絕大部分生命週期都會以非常大的方式影響CPU嗎?

+0

您認爲有多少線程可能在等待?幾百個左右的內核可能不會對核心檢查線程以及他們需要安排的時間徵稅,但如果您排隊等待超過500個,您可能需要重新考慮您的方法.. – txtechhelp

+0

我可能會有超過幾百人。你能解釋爲什麼內核必須經常檢查'TIMED_WAIT'上的線程嗎?我試圖找到有關內核如何執行此操作的信息,但無法找到任何所需的信息。 – PhotometricStereo

+1

ScheduledExecutor /一個優先級隊列通過單線程到期的時間戳。 – zapl

回答

9

首先,Java(或.NET)線程!=內核/ OS線程。

一個Java Thread是一個高級包裝器,它抽象了系統線程的一些功能;這些類型的線程也被稱爲託管線程。在內核級別,一個線程只有2個狀態,運行而不運行。有一些內核跟蹤的管理信息(堆棧,指令指針,線程ID等),但是在內核級別上沒有這樣的事情,因爲線程處於TIMED_WAITING狀態(.NET等價於WaitSleepJoin州)。這些「狀態」只存在於這些類型的上下文中(爲什麼C++ std::thread沒有state成員)的一部分。儘管如此,當一個託管線程被阻塞時,這是通過幾種方式完成的(取決於它如何在託管級別被請求阻塞);我在OpenJDK中爲線程代碼看到的實現利用信號量來處理被管理的等待(這是我在其他C++框架中看到的,這些C++框架有一種「受管理」線程類以及.NET Core庫),併爲其他類型的等待/鎖使用互斥鎖。由於大多數實現都會使用某種鎖定機制(如信號量或互斥鎖),因此內核通常會做同樣的事情(至少在您的問題涉及到的地方)。也就是說,內核會將線程從「運行」隊列中取出並放入「等待」隊列(一個context switch)。進入線程調度,特別是內核如何處理線程的執行超出了這個問題的範圍,尤其是因爲您的問題在於Java,Java可以在多種不同類型的OS上運行(每個處理完全不同的線程)。

更直接地回答你的問題:

是否大量線程的JVM消耗大量的資源(內存,CPU),當線程TIMED_WAIT狀態(不睡覺)> 99.9%的時間內的?爲此,有兩點需要注意:創建的線程爲JVM(堆棧,ID,垃圾收集器等)消耗內存,內核使用內核內存來管理內核中的線程水平。消耗的內存不會改變,除非你特別說明。所以如果線程正在休眠或運行,內存是相同的。

CPU將根據線程活動和請求的線程數量進行更改(請記住,線程也會消耗內核資源,因此必須在內核級別進行管理,因此必須處理的線程越多,管理它們必須消耗更多的內核時間)。請注意,計劃和運行線程的內核時間非常短(這是設計要點的一部分),但如果您計劃運行批次的線程,它仍然是需要考慮的事情;但是,如果您計劃運行線程的批次,此外,如果您知道您的應用程序將在只有少量內核的CPU(或羣集)上運行,則可供您使用的內核越少,內核越有必要進行上下文切換,從而增加額外的時間。

當線程在等待時,如果需要的話,維護它們需要多少CPU開銷?

無。請參閱上文,但用於管理線程的CPU開銷不會根據線程上下文而改變。額外的CPU可能用於上下文切換,當CPU處於活動狀態時,線程本身會使用額外的CPU,但CPU對沒有額外的「成本」,因此可以維持的等待線程與正在運行的線程。

答案也適用於非JVM相關環境(如Linux內核)嗎?

是和否。如上所述,受管理的上下文通常適用於大多數這些類型的環境(例如,Java,.NET,PHP,Lua等),但是這些上下文可以變化,並且線程習慣用法和一般功能取決於正在使用的內核。因此,雖然一個特定的內核可能能夠爲每個進程處理1000多個線程,但有些可能有嚴格的限制,而其他問題可能會遇到其他問題,每個進程的線程數較高;您必須參考OS/CPU規格,瞭解您可能具有的限制。由於大多數線程都處於TIMED_WAIT狀態(Java的Timer()調用Object.wait(long)方法),因此絕大多數線程的生命週期仍然以非常大的方式影響CPU ?

否(被阻塞的線程的一部分),但要考慮的事情:如果(邊緣情況)所有(或> 50%)的線程需要同時運行?如果你只有幾個線程管理你的軟件包,那可能不是問題,但是說你有500+; 250線程同時被喚醒將導致大量的CPU爭用。

由於您尚未發佈任何代碼,因此很難對您的場景提出具體建議,但有人會傾向於將屬性結構存儲爲類,並將該類保存在列表或散列映射中,該映射可以是在Timer(或單獨的線程)中引用以查看當前時間是否與包的過期時間相匹配,那麼將會運行「過期」代碼。這將線程數減少到1,訪問時間減少到O(1);但是,如果沒有代碼,該建議可能無法在您的方案中發揮作用。

希望有所幫助。

+0

很好的解釋。 – hakunami