Log4j2 漏洞檢測(cè)工具清單
凡事都有其限度,對(duì)吧?汽車只能開這么快,進(jìn)程只能使用這么多內(nèi)存,程序員只能喝這么多咖啡。我們的生產(chǎn)力受到資源的限制,我們有能力更好或更差地利用它們。盡可能接近其極限使用我們的每一種資源是我們的目標(biāo),我們希望使用我們的 CPU 和內(nèi)存的每一點(diǎn),否則我們會(huì)為昂貴的機(jī)器多付錢。
然而,若是我們使用了過(guò)多的資源,我們就有可能導(dǎo)致性能問(wèn)題、服務(wù)不可用問(wèn)題和程序宕機(jī)底崩潰問(wèn)題。軟件開發(fā)看似簡(jiǎn)單,但一旦遇到性能問(wèn)題,就會(huì)變得非常棘手,這就是我們今天要討論的內(nèi)容。
定義最佳基準(zhǔn)
讓我們嘗試描述我們的最佳應(yīng)用程序行為。假設(shè)我們有許多服務(wù)器機(jī)器需要處理高吞吐量的請(qǐng)求。為簡(jiǎn)單起見,讓我們暫時(shí)忘記高峰時(shí)間或周末。我們的服務(wù)器負(fù)載在一天中的所有時(shí)間都或多或少相同。我們?yōu)檫@些服務(wù)器機(jī)器支付了很多錢,我們希望從它們那里獲得盡可能多的價(jià)值,這意味著處理盡可能多的請(qǐng)求。按照我們對(duì)簡(jiǎn)單性的承諾,我們還假設(shè)服務(wù)器僅使用內(nèi)存和 CPU 來(lái)處理所述請(qǐng)求,并且沒(méi)有其他瓶頸,例如慢速網(wǎng)絡(luò)或鎖爭(zhēng)用。
在所描述的場(chǎng)景中,我們的最佳行為是在任何給定時(shí)間使用盡可能多的 CPU 和內(nèi)存,對(duì)嗎?這樣,我們可以用更少的機(jī)器來(lái)處理相同數(shù)量的請(qǐng)求。但是您可能不想利用這些資源中的 99.9%,因?yàn)樨?fù)載的輕微增加可能會(huì)導(dǎo)致性能問(wèn)題、服務(wù)器崩潰、數(shù)據(jù)丟失和其他令人頭疼的問(wèn)題。所以我們應(yīng)該選擇一個(gè)有足夠緩沖問(wèn)題的數(shù)值。平均 85% 或 90% 的 CPU 和內(nèi)存利用率聽起來(lái)是正確的。
我們應(yīng)該首先優(yōu)化什么?
我們的應(yīng)用程序不是為平等利用 CPU 和內(nèi)存而構(gòu)建的。或者到它托管的機(jī)器的確切限制。因此,您首先應(yīng)該查看的是您的服務(wù)器是CPU-bound還是Memory-bound。當(dāng)服務(wù)器受 CPU 限制時(shí),這意味著服務(wù)器可以處理的吞吐量受到其 CPU 的限制。換句話說(shuō),如果您嘗試處理更多請(qǐng)求,CPU 將在其他資源(如內(nèi)存)達(dá)到其限制之前達(dá)到 100%。同樣的邏輯也適用于Memory-bound服務(wù)器。服務(wù)器的吞吐量將受到它可以分配的內(nèi)存的限制,當(dāng)嘗試處理更多負(fù)載時(shí),在其他資源(如 CPU)達(dá)到其限制之前,該內(nèi)存將達(dá)到 100%。
還有其他資源可以限制服務(wù)器,例如I/O,在這種情況下,吞吐量會(huì)受到磁盤或網(wǎng)絡(luò)的讀取或?qū)懭胂拗啤5俏覀儗⒃谶@篇文章中忽略這一點(diǎn),樂(lè)觀地假設(shè)我們的 I/O 是快速且無(wú)限的。
一旦你知道是什么限制了你的服務(wù)器的性能,你就會(huì)知道首先要嘗試和優(yōu)化什么。如果您的服務(wù)器受 CPU 限制,那么優(yōu)化內(nèi)存使用沒(méi)有意義,因?yàn)樗粫?huì)提高處理的吞吐量。事實(shí)上,它可能會(huì)損害吞吐量,因?yàn)槟赡軙?huì)因?yàn)楦嗟?CPU 利用率而提高內(nèi)存使用率。對(duì)于內(nèi)存受限的服務(wù)器也是如此,在這種情況下,您應(yīng)該在查看 CPU 之前優(yōu)化內(nèi)存使用。
測(cè)量 .NET 服務(wù)器中的 CPU 和內(nèi)存消耗
CPU 和內(nèi)存的實(shí)際測(cè)量最簡(jiǎn)單的是使用Performance Counters[1]完成。CPU 使用率的指標(biāo)是Process | % 處理器時(shí)間。內(nèi)存有幾個(gè)指標(biāo),但我建議查看Process | 私有字節(jié)。您可能還對(duì).NET CLR 內(nèi)存感興趣 | # 代表托管內(nèi)存的所有堆中的字節(jié)(CLR 占用的部分,而不是所有內(nèi)存,即托管 + 本機(jī)內(nèi)存)。
要查看性能計(jì)數(shù)器,您可以在 Windows 計(jì)算機(jī)上使用Process Explorer[2]或 PerfMon,或者在 .NET Core 服務(wù)器上使用dotnet-counters 。[3]如果您的應(yīng)用程序部署在云中,您可以使用像Application Insights[4](Azure Monitor[5]的一部分)這樣的 APM 工具來(lái)顯示這些信息。或者,您可以在代碼中獲取性能計(jì)數(shù)器值并每 10 秒左右記錄一次,使用Azure 數(shù)據(jù)資源管理器[6]之類的工具在圖表中顯示數(shù)據(jù)。
提示:檢查機(jī)器級(jí)指標(biāo)和進(jìn)程級(jí)指標(biāo)。您可能會(huì)發(fā)現(xiàn)其他進(jìn)程正在限制您的性能。
一旦確定了哪些資源限制了您的 .NET 服務(wù)器,就該優(yōu)化該資源消耗了。如果您受 CPU 限制,讓我們減少 CPU 使用率。如果您受內(nèi)存限制,讓我們減少內(nèi)存使用量。
至少如果您在云中運(yùn)行,一種簡(jiǎn)單的方法是更改機(jī)器規(guī)格。如果您受內(nèi)存限制,請(qǐng)?jiān)黾觾?nèi)存。如果您受 CPU 限制,請(qǐng)?jiān)黾觾?nèi)核數(shù)量或獲得更快的 CPU。這將提高成本,但在此之前,您可以檢查一些容易實(shí)現(xiàn)的目標(biāo),以優(yōu)化 CPU 或內(nèi)存消耗。在更改機(jī)器規(guī)格之前嘗試進(jìn)行這些優(yōu)化,因?yàn)閮?yōu)化后一切都會(huì)改變。您可能會(huì)優(yōu)化 CPU 使用率并變得受內(nèi)存限制。然后優(yōu)化內(nèi)存使用并再次成為 CPU 密集型。因此,如果您想避免不得不不斷更改機(jī)器資源以適應(yīng)最新的優(yōu)化,最好把它留到最后。
所以讓我們談?wù)勔恍﹥?nèi)存優(yōu)化。
優(yōu)化內(nèi)存使用
有很多方法可以優(yōu)化 .NET 中的內(nèi)存使用。深入討論它們需要一整本書,而且已經(jīng)有好幾本了。但我會(huì)盡量給你一些方向和想法。
1. 了解什么占用了你的內(nèi)存
嘗試優(yōu)化內(nèi)存時(shí),您應(yīng)該做的第一件事是了解全局。什么占用了大部分內(nèi)存?有哪些數(shù)據(jù)類型?它們分配在哪里?它們會(huì)在記憶中停留多久?
有幾種工具可以獲取此信息:
- 捕獲轉(zhuǎn)儲(chǔ)文件[7]并使用內(nèi)存分析器[8]或WinDbg[9]打開它。
- 使用新的GC 轉(zhuǎn)儲(chǔ)[10](.NET Core 3.1+) 并使用 Visual Studio 進(jìn)行調(diào)查。
- 捕獲堆快照并使用內(nèi)存分析器[11]、PerfView[12]或Visual Studio 診斷工具[13]對(duì)其進(jìn)行探索。
此分析將顯示哪些對(duì)象占用了您的大部分內(nèi)存。如果你發(fā)現(xiàn)它被采取了MyProgram.CustomerData那就更好了。但通常,最大的對(duì)象類型是string、byte[]或byte[][]。由于應(yīng)用程序中的幾乎所有內(nèi)容都可以使用這些類型,因此您需要找到引用它們的人。為此,查看所占用的包容性內(nèi)存(又名保留內(nèi)存)很重要。這個(gè)指標(biāo)不僅包括對(duì)象本身占用的內(nèi)存,還包括它引用的對(duì)象占用的內(nèi)存。例如,您可能會(huì)發(fā)現(xiàn)它MyProgram.Inventory.Item本身并不占用太多內(nèi)存,但它引用了一個(gè)byte[]它保存內(nèi)存中的圖像并占用高達(dá) 70% 的內(nèi)存。上面描述的所有工具都可以顯示包含最多字節(jié)的對(duì)象和到 GC 根的引用路徑(也就是到根的最短路徑[14])。
2. 了解誰(shuí)把內(nèi)存放在了哪里
找出誰(shuí)引用了最大的內(nèi)存塊很棒,但這可能還不夠。有時(shí)您需要知道這些內(nèi)存是如何分配的。您可能從引用路徑中知道,一些占用大部分內(nèi)存的對(duì)象位于緩存中,但誰(shuí)將它們放在那里?來(lái)自單個(gè)時(shí)間點(diǎn)的內(nèi)存快照無(wú)法提供該答案。為此,您需要分配堆棧跟蹤。分析器使您能夠記錄您的應(yīng)用程序并在每次分配時(shí)保存調(diào)用堆棧。例如,您可能會(huì)發(fā)現(xiàn)創(chuàng)建有問(wèn)題MyProgram.Inventory.Item對(duì)象的流程將它們分配到調(diào)用堆棧App.OnShowHistoryClicked | App.SeeItemHistory | App.GetItemFromDatabase中。
要獲得分配堆棧,您可以:
- 使用商業(yè)內(nèi)存分析器來(lái)顯示分配[15]。
- 使用 PerfView 的 GC Heap [] Stacks 之一
分配讓您全面了解占用大部分內(nèi)存的內(nèi)容以及它是如何產(chǎn)生的。一旦你知道了這一點(diǎn),你就可以開始切割最大的塊并優(yōu)化它們以減少內(nèi)存使用。
3.檢查內(nèi)存泄漏
在 .NET 中導(dǎo)致內(nèi)存泄漏非常容易。有了足夠多的泄漏,內(nèi)存消耗會(huì)隨著時(shí)間的推移而增加,你會(huì)遇到各種各樣的問(wèn)題。內(nèi)存瓶頸就是其中之一,但由于 GC 壓力,您最終也會(huì)遇到 CPU 問(wèn)題。
當(dāng)您不再需要對(duì)象但由于某種原因它們?nèi)匀槐灰貌⑶依占饔肋h(yuǎn)不會(huì)釋放它們時(shí),就會(huì)發(fā)生內(nèi)存泄漏。發(fā)生這種情況的原因[16]有很多。
要了解您是否有嚴(yán)重的內(nèi)存泄漏,請(qǐng)查看一段時(shí)間內(nèi)的內(nèi)存消耗圖表(進(jìn)程 | 私有字節(jié)計(jì)數(shù)器)。如果內(nèi)存一直在增加,而沒(méi)有偏離某個(gè)水平,則可能存在內(nèi)存泄漏。
使用內(nèi)存分析器調(diào)試泄漏[17]相當(dāng)簡(jiǎn)單。
4. 切換到 GC 工作站模式
.NET 中有幾種垃圾收集器模式。主要的兩種模式是Workstation GC和Server GC。Workstation GC 針對(duì)更短的 GC 暫停和更快的交互性進(jìn)行了優(yōu)化,非常適合桌面應(yīng)用程序。服務(wù)器 GC 具有更長(zhǎng)的 GC 暫停時(shí)間,并且針對(duì)更高的吞吐量進(jìn)行了優(yōu)化。在 Server GC 模式下,應(yīng)用程序可以在垃圾回收之間處理更多數(shù)據(jù)。
服務(wù)器 GC 為每個(gè) CPU 核心創(chuàng)建不同的托管堆。這意味著不同的 X 代內(nèi)存空間需要更長(zhǎng)的時(shí)間才能填滿,因此內(nèi)存消耗會(huì)更高。您基本上是在用內(nèi)存換取吞吐量。從 GC 服務(wù)器模式(.NET 服務(wù)器的默認(rèn)模式)更改為 GC 工作站模式將減少內(nèi)存使用量。這在請(qǐng)求負(fù)載不重的小型應(yīng)用程序中可能是合理的。也許在與主應(yīng)用程序一起運(yùn)行的 IIS 主機(jī)中的輔助進(jìn)程中。
Sergey Tepliakov[18]對(duì)此有一篇很棒的文章。
5.檢查你的緩存
在第 1 步之后,您應(yīng)該能夠看到哪些對(duì)象占用了您的內(nèi)存,但我想特別強(qiáng)調(diào)緩存。每當(dāng)涉及到高內(nèi)存消耗時(shí),根據(jù)我的經(jīng)驗(yàn),它總是最終成為內(nèi)存泄漏或緩存。
緩存似乎是許多問(wèn)題的神奇解決方案。當(dāng)您可以將結(jié)果保存在內(nèi)存中并重新使用它時(shí),為什么要執(zhí)行兩次?但是緩存是有代價(jià)的。一個(gè)簡(jiǎn)單的實(shí)現(xiàn)會(huì)將對(duì)象永遠(yuǎn)保存在內(nèi)存中。您應(yīng)該按時(shí)間限制或以其他方式使緩存無(wú)效。緩存還會(huì)將臨時(shí)對(duì)象留在內(nèi)存中相對(duì)較長(zhǎng)的時(shí)間,這會(huì)導(dǎo)致更多的 Gen 1 和 Gen 2 收集,進(jìn)而導(dǎo)致GC 壓力[19]。
以下是一些優(yōu)化內(nèi)存緩存的想法:
- 使用.NET 中的現(xiàn)有緩存實(shí)現(xiàn)[20]可以輕松創(chuàng)建失效策略。
- 考慮為某些事情選擇不緩存。您可能會(huì)用 CPU 或 IO 換取內(nèi)存,但是當(dāng)您受到內(nèi)存限制時(shí),您應(yīng)該這樣做。
- 考慮使用內(nèi)存不足緩存。這可能是將數(shù)據(jù)保存在文件或本地?cái)?shù)據(jù)庫(kù)中。或者使用像Redis[21]這樣的分布式緩存解決方案。
6.定期調(diào)用GC.Collect()
這條建議是違反直覺(jué)的,因?yàn)樽詈玫淖龇ㄊ怯肋h(yuǎn)不要調(diào)用GC.Collect(). 垃圾收集器很聰明,它應(yīng)該自己知道何時(shí)觸發(fā)收集。但問(wèn)題是垃圾收集器只考慮自己的進(jìn)程。如果它沒(méi)有足夠的內(nèi)存,它會(huì)小心觸發(fā)收集并騰出空間。但如果它確實(shí)有足夠的內(nèi)存,GC 會(huì)非常樂(lè)意忍受過(guò)多的內(nèi)存消耗。因此,GC 的自私本性可能是生活在同一臺(tái)機(jī)器上的其他進(jìn)程的問(wèn)題,可能托管在同一個(gè) IIS 上。這種多余的內(nèi)存可能會(huì)導(dǎo)致其他進(jìn)程更快地達(dá)到它們的極限,或者導(dǎo)致它們各自的垃圾收集器更加努力地工作,因?yàn)樗鼈兛赡苠e(cuò)誤地認(rèn)為它們即將耗盡內(nèi)存。
您可能會(huì)認(rèn)為,如果其他進(jìn)程的 GC 會(huì)達(dá)到認(rèn)為我們內(nèi)存不足并因此更加努力地工作的程度,那么我們自己的進(jìn)程也會(huì)這樣認(rèn)為并觸發(fā)垃圾收集來(lái)解決問(wèn)題。但我們不能做出這樣的假設(shè)。一方面,這些進(jìn)程可能運(yùn)行不同的 GC 實(shí)現(xiàn)版本(因?yàn)椴煌?CLR 版本)。此外,您有不同的應(yīng)用程序行為可以使 GC 以不同的方式工作。例如,一個(gè)進(jìn)程可能會(huì)以更高的速率分配內(nèi)存,因此 GC 將更快地開始“強(qiáng)調(diào)”可用內(nèi)存。底線是軟件很困難,當(dāng)你在一臺(tái)機(jī)器上有多個(gè)進(jìn)程時(shí),就像 IIS 一樣,你需要考慮到這一點(diǎn),并可能采取一些不尋常的步驟。
優(yōu)化 CPU 使用率
硬幣的另一面是 CPU 使用率。一旦您發(fā)現(xiàn) CPU 是應(yīng)用程序吞吐量的瓶頸,就需要做很多事情。
1. 分析您的應(yīng)用程序
優(yōu)化 CPU 的第一步是了解它。究竟是什么原因造成的?哪些方法負(fù)責(zé)?哪些請(qǐng)求是最大的 CPU 消耗者,哪些是流量?這一切都可以通過(guò)分析應(yīng)用程序來(lái)解決。
分析允許您記錄執(zhí)行范圍并顯示所有被調(diào)用的方法以及它們?cè)谟涗浧陂g使用了多少 CPU。分析器通常允許將這些結(jié)果視為普通列表、調(diào)用樹甚至火焰圖。
這是 PerfView 中的簡(jiǎn)單列表視圖:
這是相同場(chǎng)景的火焰圖:
您可以通過(guò)以下方式分析您的應(yīng)用:
- 如果場(chǎng)景在本地重現(xiàn),請(qǐng)使用性能分析器,如PerfView[22]、dotTrace[23]、ANTS perf profiler[24],或在您的開發(fā)計(jì)算機(jī)上使用 Visual Studio 。[25]
- 在生產(chǎn)環(huán)境中,最簡(jiǎn)單的分析方法是使用應(yīng)用程序性能監(jiān)控 (APM) 工具,例如Azure Application Insights profiler[26]或RayGun[27]。
- 您可以通過(guò)將代理復(fù)制到生產(chǎn)機(jī)器并記錄快照來(lái)分析沒(méi)有 APM 的生產(chǎn)環(huán)境。使用 PerfView,您應(yīng)該復(fù)制整個(gè)程序。它結(jié)構(gòu)緊湊,無(wú)需安裝。使用 dotTrace,您可以復(fù)制允許在生產(chǎn)中記錄快照的輕量級(jí)代理。[28]
- 在 .NET Core 3.0+ 應(yīng)用程序中,您可以安裝 .NET Core 3.0 SDK 并使用 dotnet-trace 命令行工具記錄快照[29],然后使用 PerfView 將其復(fù)制到開發(fā)機(jī)器并進(jìn)行分析。
2.檢查垃圾收集器的使用情況
我想說(shuō)優(yōu)化 .NET CPU 使用最重要的一點(diǎn)是正確的內(nèi)存管理。在這方面要問(wèn)的重要問(wèn)題是:“垃圾收集浪費(fèi)了多少 CPU?”。GC 的工作方式是在收集期間,您的執(zhí)行線程被凍結(jié)。這意味著垃圾收集直接影響性能。因此,如果您受 CPU 限制,我建議您檢查的第一件事是性能計(jì)數(shù)器[30]。NET CLR 內(nèi)存 | % GC 時(shí)間。
我不能給你一個(gè)指示問(wèn)題的神奇數(shù)字,但根據(jù)經(jīng)驗(yàn),當(dāng)這個(gè)值超過(guò) 20% 時(shí),你可能會(huì)遇到問(wèn)題。如果超過(guò) 40%,那么你肯定有問(wèn)題。如此高的百分比表明 GC 壓力,并且有辦法處理它[31]。
3.使用數(shù)組和對(duì)象池來(lái)重用內(nèi)存
陣列的分配和不可避免的解除分配可能非常昂貴。高頻率執(zhí)行這些分配會(huì)造成 GC 壓力并消耗大量 CPU 時(shí)間。解決這個(gè)問(wèn)題的一個(gè)好方法是使用內(nèi)置的ArrayPoolObjectPool ([32]僅限 .NET Core)。這個(gè)想法很簡(jiǎn)單。為數(shù)組或?qū)ο蠓峙湟粋€(gè)共享緩沖區(qū),然后在不分配和取消分配新內(nèi)存的情況下重復(fù)使用。這是一個(gè)簡(jiǎn)單的使用示例ArrayPool:
public void Foo()
{
var pool = ArrayPool<int>.Shared;
int[] array = pool.Rent(ArraySize);// do stuf
pool.Return(array);
}
4. 切換到 GC 服務(wù)器模式
我們已經(jīng)討論過(guò)轉(zhuǎn)移到GC 工作站模式[33]以節(jié)省內(nèi)存。但如果您受 CPU 限制,請(qǐng)考慮切換到服務(wù)器模式以節(jié)省 CPU。權(quán)衡是服務(wù)器模式以更多內(nèi)存為代價(jià)允許更高的吞吐量。因此,如果您保持相同的吞吐量,您最終將節(jié)省 CPU 時(shí)間,否則垃圾收集會(huì)花費(fèi)這些時(shí)間。
默認(rèn)情況下,.NET 服務(wù)器很可能具有 GC 服務(wù)器模式,因此可能不需要此更改。但是可能有人之前將其更改為工作站模式,在這種情況下,您應(yīng)該小心將其更改回來(lái),因?yàn)樗麄兛赡苡谐浞值睦碛伞?/p>
更改時(shí),請(qǐng)務(wù)必監(jiān)控內(nèi)存消耗和 GC 中的 % Time。您可能想查看第 2 代回收率,但如果這個(gè)數(shù)字很高,它將反映在更高的 GC 時(shí)間百分比中。
5.檢查其他進(jìn)程
當(dāng)試圖將您的服務(wù)器發(fā)揮到最佳極限時(shí),您可能想要徹底了解它,這意味著不要放棄存在于您的進(jìn)程之外的問(wèn)題。很有可能其他進(jìn)程不時(shí)消耗一堆CPU,并導(dǎo)致一段時(shí)間的性能下降。這些可能是您在 IIS 上部署的其他應(yīng)用程序、定期 Web 作業(yè)、由操作系統(tǒng)觸發(fā)的東西、防程序或其他一千種東西。
對(duì)此進(jìn)行分析的一種方法是使用 PerfView 記錄整個(gè)系統(tǒng)中的 ETW 事件。PerfView 從
所有
進(jìn)程中捕獲 CPU 堆棧。您可以以很小的性能開銷運(yùn)行它很長(zhǎng)時(shí)間。您可以在達(dá)到某個(gè) CPU 峰值時(shí)自動(dòng)停止收集[34]并進(jìn)行挖掘。您可能會(huì)對(duì)結(jié)果感到驚訝。
總結(jié)
在我看來(lái),從自上而下的層面處理大規(guī)模的性能問(wèn)題是令人著迷的。您可能有一個(gè)團(tuán)隊(duì)花費(fèi)數(shù)月時(shí)間優(yōu)化一段代碼,相比之下,資源分配的簡(jiǎn)單更改將產(chǎn)生更大的影響。而且,如果您的業(yè)務(wù)足夠大,那么這個(gè)微小的變化就會(huì)轉(zhuǎn)化為一大筆錢。你記得在你的合同中要求一個(gè)傭金條款嗎?無(wú)論如何,我希望這篇文章對(duì)你有用,如果你發(fā)現(xiàn)了,你可能會(huì)對(duì)我的書Practical Debugging for .NET 開發(fā)人員[35]感興趣,我在其中深入討論了性能和內(nèi)存問(wèn)題的故障排除。
References
[1] Performance Counters: https://michaelscodingspot.com/performance-counters/
[2] Process Explorer: https://docs.microsoft.com/en-us/sysinternals/downloads/process-explorer
[3] dotnet-counters 。: https://docs.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-counters
[4] Application Insights: https://docs.microsoft.com/en-us/azure/cloud-services/diagnostics-performance-counters#application-insights
[5] Azure Monitor: https://azure.microsoft.com/en-us/services/monitor/#overview
[6] Azure 數(shù)據(jù)資源管理器: https://azure.microsoft.com/en-us/services/data-explorer/#getting-started
[7] 捕獲轉(zhuǎn)儲(chǔ)文件: https://michaelscodingspot.com/how-to-create-use-and-debug-net-application-crash-dumps-in-2019/
[8] 內(nèi)存分析器: https://memprofiler.com/online-docs/manual/importmemorydumpfiles.html
[9] WinDbg: https://michaelscodingspot.com/how-to-create-use-and-debug-net-application-crash-dumps-in-2019/#Investigate-Dumps-with-WinDbg
[10] GC 轉(zhuǎn)儲(chǔ): https://devblogs.microsoft.com/dotnet/collecting-and-analyzing-memory-dumps/
[11] 內(nèi)存分析器: https://michaelscodingspot.com/memory-profilers-principles#snapshots
[12] PerfView: https://bennettadelson.wordpress.com/2013/04/11/using-perfview-to-diagnose-a-net-memory-leak-2/
[13] Visual Studio 診斷工具: https://docs.microsoft.com/en-us/visualstudio/profiling/memory-usage?view=vs-2022
[14] 到根的最短路徑: https://www.jetbrains.com/help/dotmemory/Shortest_Paths_to_Roots.html
[15] 內(nèi)存分析器來(lái)顯示分配: https://www.jetbrains.com/help/dotmemory/Analyze_Memory_Allocation.html#types
[16] 發(fā)生這種情況的原因: https://michaelscodingspot.com/ways-to-cause-memory-leaks-in-dotnet/
[17] 使用內(nèi)存分析器調(diào)試泄漏: https://michaelscodingspot.com/find-fix-and-avoid-memory-leaks-in-c-net-8-best-practices/#profiler
[18] Sergey Tepliakov: https://devblogs.microsoft.com/premier-developer/understanding-different-gc-modes-with-concurrency-visualizer/
[19] GC 壓力: https://michaelscodingspot.com/avoid-gc-pressure/
[20] .NET 中的現(xiàn)有緩存實(shí)現(xiàn): https://michaelscodingspot.com/cache-implementations-in-csharp-net/
[21] Redis: https://redis.io/
[22] PerfView: https://github.com/microsoft/perfview
[23] dotTrace: https://www.jetbrains.com/profiler/
[24] ANTS perf profiler: https://www.red-gate.com/products/dotnet-development/ants-performance-profiler/
[25] 使用 Visual Studio 。: https://docs.microsoft.com/en-us/visualstudio/profiling/beginners-guide-to-performance-profiling?view=vs-2022
[26] Azure Application Insights profiler: https://docs.microsoft.com/en-us/azure/azure-monitor/app/profiler-overview
[27] RayGun: https://raygun.com/for/dotnet-performance-monitoring
[28] 的輕量級(jí)代理。: https://blog.jetbrains.com/dotnet/2012/09/10/dottrace-remote-profiling/
[29] 使用 dotnet-trace 命令行工具記錄快照: https://michaelscodingspot.com/dotnet-trace/
[30] 性能計(jì)數(shù)器: https://michaelscodingspot.com/performance-counters/
[31] 辦法處理它: https://michaelscodingspot.com/avoid-gc-pressure/
[32] (: https://docs.microsoft.com/en-us/aspnet/core/performance/objectpool?view=aspnetcore-6.0
[33] GC 工作站模式: https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/workstation-server-gc
[34] 在達(dá)到某個(gè) CPU 峰值時(shí)自動(dòng)停止收集: https://www.drware.com/perfview-command-for-capturing-automated-high-cpu-dumps/
[35] Practical Debugging for .NET 開發(fā)人員: https://practicaldebugging.net/