中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

作為 .Net 攻城師,所必需掌握的 .Net Profiling 技術

發布時間:2020-07-02 15:26:18 來源:網絡 閱讀:344 作者:OneAPM123 欄目:編程語言

眾所周知,性能問題是所有實用應用在迭代過程中必然要面對的問題。對于此類問題,簡單地投入更多硬件資源的做法可能會取得一定效果。但總的來看,此類做法的邊際成本是不斷上升的。換言之,隨著性能需求的上漲,要換取同樣的性能提升,僅憑硬件升級所需要的成本會越來越高。故而性能優化是每一位運維/軟件開發人員必須掌握的技術。

.Net Profiling

在進行應用性能優化實踐時,首先面對的就是熱點定位,即確定那些帶來巨大資源耗散的代碼位置。而在不借助外部工具的前提下,定位資源熱點是一件相當困難的事。它需要當事人對于應用實現本身有一個整體的把握,了解應用架構內每一個功能模塊代碼的路徑與細節。與此同時,當事人還要對于應用實現所依賴的第三方功能庫的表現有一定的把握。對于那些具備一定規模的應用系統,具備前述素質的工程師的數量屈指可數。而即便是這些百里挑一的優秀人才,其熱點預估也不能保證一定是準確的。

Profiling 技術 的提出正是為了解決熱點定位而提出的,它以程序的實際運行數據來幫助工程師們來理解應用行為,極大地簡化了工程師們的工作。而對于像 .Net 這樣比較成熟的技術棧,專家們(如 Bill Chiles 和 趙頡)都推薦工程人員通過實際的 Profiling 數據來定位性能瓶頸。實際操作中,獲取 .Net Profiling 數據的功能被實現為一個被稱為 Profiler 的 COM 組件實體。下文中,我們將就 Profiler 本身的實現進行一些探討。

Profiling API

.Net Profiler 在本質上是 CLR 的一個插件,它通過應用 Profiling API 來保持與 CLR 的信息溝通,并以此獲取到 .Net 應用的運行時數據。通常來說,Profiler 的實體都表現為一個動態鏈接庫(即 .dll 文件),CLR 在運行時會去加載該庫(CLR 加載 Profiler 的詳細配置可以參考 這篇文檔),并在程序運行的特定階段向庫發送信息并接受庫所返回的信息。

需要特別強調的一點是,雖然被稱作是 Profiling API,但 CLR 的這套接口能做的可不僅僅是簡單地度量應用的運行時間和內存耗散。實踐上,profiling API 能夠完成諸如代碼覆蓋、運行時插入等許多高級功能。不過,正如 MSDN Profiling 綜述文檔 所強調的,Profiling 對于應用本身應該透明。也就是說,在編寫應用的時候,開發人員不應該在自己的邏輯中依賴 Profiler 或者被其影響。

對于 .Net 技術棧而言,由于環境本身引入了 application domain, GC, managed exception handling, JIT 等高級特性,Profiling 所展現的就不能僅止于應用運行所消耗的時間或者內存。為了能夠真正地表現出運行時行為,Profiling API 中提供了包含這些特性的數據的接口。這就使得 Profiling API 在設計并不是如很多人想得那樣直觀。

常見的 .Net Profiler 實現多采取如下架構: 
作為 .Net 攻城師,所必需掌握的 .Net Profiling 技術 
.Net Profiler 架構

其中,ICorProfilerCallback 與 ICorProfilerInfo 就是 Profiling API 中最常被應用到的兩個。在應用運行時,Profiler DLL 會被加載到應用所在的進程中。通過實現 ICorProfilerCallback 下特定功能的接口,Profiler DLL 會在應用運行時收到相關的動作執行通知。例如,如果在 Profiler DLL 中實現了 ICorProfilerCallback::AssemblyLoadFinished 接口,那么在應用運行中每加載完一個程序集 時,Profiler DLL 中該接口的實現代碼就會被調用。與此類似,Profiler DLL 可以靠實現 ICorProfilerInfo 下的接口來完成對被監測應用狀態的獲取。

需要補充說明一點,上文所說的 ICorProfilerCallback 接口實際上存在有 ICorProfilerCallback ~ ICorProfilerCallback7 這樣7個版本的接口定義。高標號的接口版本向下兼容,但會提供新的功能擴展。不過,更高標號的接口往往也需要有更新版本的 CLR 來支持(如調用 ICorProfilerCallback7 需要在環境中部署 .Net Framework 4.6.1 以上版本),在實際使用時需要多加注意。

目前,Profiling API 可以被任何非托管的 COM 兼容的語言所調用。另外,API 本身的實現非常高效的,不會帶來大到足以導致 profiling 失效的額外性能負擔。也正因此,基于 Profiling API 完全可以實現一個抽樣 profiler(對于 profiling 模式的探討可參照 這篇文獻 的 2.1 章節內容)。

目前 Profiling API 所支持的特性

正如前文所述,Profiler 對于程序行為的描述源自 profiling API 所提供的信息。在目前版本中,憑借 profiling API 能夠獲取到下列事件的消息通知:

  1. CLR 的啟動與關停

  2. application domain 的創建與關閉

  3. 程序集的加載與卸載

  4. 模塊(Module)的加載與卸載

  5. COM vtable 的創建與銷毀

  6. JIT 編譯與 code-pitching 的出發

  7. 類的加載卸載

  8. 線程的創建與銷毀

  9. 函數的進入與返回

  10. 托管代碼與非托管代碼的執行切換

  11. 運行時掛起

  12. 運行時堆內存信息與 GC 活動

隨著 .Net 技術的演進,未來的 Profiling API 或許能夠提供更多的信息。不過,以下功能點是 Profiling API 不會實現的,請在應用時回避:

  1. 非托管代碼的執行信息

  2. 運行時修改自身代碼的應用的 Profiling(如 AOP)

  3. 邊界檢驗

  4. 遠程 profiling

  5. 高可靠性環境下的 profiling

線程相關

對于加載了 Profiler DLL 的進程而言,其在創建新線程時,新線程本身也會產生 ICorProfilerCallback 接口下定義的各種事件通知。這一過程中,Profiler 不必去顯式地指定一個 ThreadID 以使得 Profiling API 生效。同樣的,Profiler 完全可以簡單地在代碼中使用 thread-local 的存儲方式,用不著費心地去進行存儲位置的全局重定向。

當然,還是有一些要點需要我們在并發背景下留意。例如,Profiling API 本身并不能保障數據結構的線程安全,因此我們需要在可能產生并行訪問的地方給 Profiler 代碼沖突區加鎖以保證 Profiler 的行為符合預期。同樣,對于多線程的應用場景來說,Profiler 不應該假設 ICorProfilerCallback 的各個接口存在一定的先后順序。舉例來說,一個有兩個同樣線程的程序在運行時可能會先產生一個 FunctionEnter 然后才產生 ICorProfilerCallback::JITCompilationFinished。

還有一個線程相關的問題是來自于 COM 接口的。上文中我們說過 Profiler 事實上是實現為一個 COM 組件的,但其實 CLR 在運行時并不會去初始化 COM。這是為了避免在應用代碼指定線程模型前,CLR 調用 [CoInitialize][Ref12] 來指定應用線程模型。同樣地,在 Profiler 內部,不要去調用 CoInitialize 以避免與應用代碼產生沖突。

調用棧

獲取調用棧信息是應用 Profiling 時的一項關鍵需求。針對這一需求,Profiling API 提供給 Profiler 編寫者兩種實現方式:棧快照和倒影棧。

棧快照是在特定時刻對特定線程調用棧的一次追蹤。需要注意,Profiling API 僅支持對棧上托管函數的追蹤。如果 Profiler 需要追蹤棧上的非托管函數,則需要自己提供一個棧遍歷器出來。讀者如對 Profiler 棧快照機制的實現詳情感興趣,可以參照 Divid Broman 的 這篇博客 內容。

頻繁使用棧快照會為 CLR 帶來過多的額外性能損耗。因此,如果需要頻繁進行棧追蹤,那么 Profiler 應該通過 FunctionEnter2, FunctionLeave2, FunctionTailCall2 及 ICorProfilerCallback::Exception* 等一系列接口構建出當前應用調用棧的一個倒影。如此,Profiler 即可低消耗地進行棧追蹤操作。

其他需要留意之處

前文強調過,Profiler 是一個非托管的 DLL 庫,會在應用運行時被加載到 CLR 中并與應用處于同一進程空間下。如此,Profiler DLL 實質上是不受托管代碼的訪問控制的。其運行唯一的限制就是運行 Profiler 的 OS 用戶必須擁有足夠權限。因此,對于要部署 Profiler 的技術人員來說,必須要明白這其中可能的風險,提早進行準備。例如可以把 Profiler DLL 加到訪問控制列表(ACL)中以免惡意用戶對其加以利用。

還有,Profiler DLL 作為 CLR 的一個插件,其運行錯誤可能會引起 CLR 本身的崩潰,在實施時一定要足夠小心。而對于那些運行在棧內存空間緊張的環境下的 Profiler,要警惕因為 ICorProfilerCallback 導致棧溢出而引起的應用崩潰。在這種資源受限的環境中,要盡可能地減少 Profiler 自身的資源耗散。盡力避免原本能夠運行的應用因為 Profiler 而導致無法運行的情況。

結語

本文簡述了 .Net Profiling 技術的總體情況,并就其中的一些重要技術點進行了闡述。希望能幫助讀者初步理解 .Net Profiling 技術。后續,我們將具體到代碼實施層面,就 .Net Profiling 的實現進行詳細討論

OneAPM 助您輕松鎖定 .NET 應用性能瓶頸,通過強大的 Trace 記錄逐層分析,直至鎖定行級問題代碼。以用戶角度展示系統響應速度,以地域和瀏覽器維度統計用戶使用情況。想閱讀更多技術文章,請訪問 OneAPM 官方博客。 
本文轉自 OneAPM 官方博客


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

元阳县| 调兵山市| 上蔡县| 上饶县| 久治县| 潞西市| 兴海县| 内江市| 亳州市| 包头市| 高陵县| 从化市| 涿鹿县| 云阳县| 射阳县| 泸州市| 鄂尔多斯市| 错那县| 方山县| 东乡县| 山阳县| 无极县| 故城县| 广宗县| 岢岚县| 固安县| 吕梁市| 肃宁县| 肇州县| 勃利县| 新泰市| 双城市| 新源县| 来宾市| 盘山县| 怀仁县| 贡觉县| 闵行区| 临泽县| 永定县| 博白县|