<em id="3iliw"></em>
  • <progress id="3iliw"></progress>
  • <tbody id="3iliw"><pre id="3iliw"></pre></tbody><dd id="3iliw"><noscript id="3iliw"></noscript></dd>

    <progress id="3iliw"></progress>

    杭州.net培訓
    達內杭州.net培訓中心

    13175137725

    .NET的反射為什么慢

    • 時間:2018-04-24 17:05
    • 發布:杭州.NET培訓
    • 來源:知識庫

    CLR類型系統的設計目標

    原因之一是,在設計的時候反射本身就不是以高性能為目標的,可以參考Type System Overview -‘Design Goals and Non-goals’(類型系統概覽-‘設計目標和非目標’):

    目標:

    ·        運行時通過快速執行(非反射)代碼訪問需要的信息。

    ·        編譯時直接訪問所需要的信息來生成代碼。

    ·        垃圾回收/遍歷棧可以訪問需要信息而不需要鎖或分配內存。

    ·        一次只加載最少量的類型。

    ·        類型加載時只加載最少需要加載的類型。

    ·        類型系統的數據結構必須在NGEN映像中保存。

    非目標:

    ·        元數據的所有信息能直接反射CLR數據結構。

    ·        快速使用反射。

    參閱出處相同的 Type Loader Design -‘Key Data Structures’(類型加載器設計-‘關鍵數據結構’):

    EEClass:

    MethodTable(方法表)數據分為“熱”和“冷”兩種結構,以提高工作集和緩存的利用率。MethodTable本身只存儲程序穩定狀態的“熱”數據。EEClass存儲“冷”數據,它們通常是類型加載、JITing或反射所需要的。每個MethodTable指向一個EEClass。

    反射是如何工作的?

    我們已經知道反射本身就不是以快為目標來設計的,但是它為什么需要那么多時間呢?

    為了說明這個問題,來看看反射調用過程中,托管代碼和非托管代碼的調用棧。

    ·        System.Reflection.RuntimeMethodInfo.Invoke(..) - 源碼鏈接

    § 調用 System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(..)

    ·        System.RuntimeMethodHandle.PerformSecurityCheck(..) - 鏈接

    § 調用 System.GC.KeepAlive(..)

    ·        System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(..) - 鏈接

    § 調用 System.RuntimeMethodHandle.InvokeMethod(..)的存根

    ·        System.RuntimeMethodHandle.InvokeMethod(..)的存根 - 鏈接

    即使不點擊鏈接,想必你也能直觀感受到改方法執行的大量代碼。參考示例:System.RuntimeMethodHandle.InvokeMethodis超過400行代碼!

    那么,它具體在做什么?

    獲取方法信息

    要使用反射來調用字段/屬性/方法,你必須獲得FieldInfo/PropertyInfo/MethodInfo,使用這樣的代碼:

    這需要一定的成本,因為需要提取相關的元數據,并對其進行解析。運行時會幫我們維持一個內部緩存,緩存著所有字段/屬性/方法。這個緩存由 RuntimeTypeCache類實現,用法示例在 RuntimeMethodInfo類中.

    運行 gist 中的代碼你可以看到緩存的何運作方式,它恰如其分地使用反射檢查運行時內部!

    gist上的代碼會在你使用反射獲得FieldInfo之前輸出下列內容:

    不過一旦你獲得字段,就會輸出:

    ReflectionOverhead.Program看起來像這樣:

    參數校驗和錯誤處理

    得到MethodInfo之后,如果調用它的 Invoke方法,會要處理很多事項。假設編寫代碼如下:

    如果運行上述代碼,會得到下面的異常:

    這是因為我們獲得了String類Length屬性的PropertyInfo,但是卻在Uri對象上調用它,顯然,這是個錯誤的類型!

    此外,你還必須在調用方法時對傳遞給方法的參數進行校驗。為了能傳遞參數,反射API使用了一個object的數組作為參數,其中每一個元素表示一個參數。所以,如果你使用反射來調用Add(int x, int y)方法,你得調用 methodInfo.Invoke(.., ew [] { 5, 6 })。運行時會對傳入參數的數量和類型進行檢查,在這個示例中你要確保是2個int類型的參數。這些工作不好的地方是常常需要裝箱,這會增加額外的成本。希望這在將來會降到最低。

    安全性檢查

    另一個主要任務是多重安全性檢查。例如,你不允許使用反射來任意調用你想調用的方法。這里存在一些限制的或 ‘危險方法’,只能由可信度高的.NET框架代碼調用。除了黑名單外,還有動態安全檢查,它由調用時必須檢查的的當前代碼訪問安全權限決定。

    反射機制耗時多少?

    了解反射的實際操作后,我們來看看實際耗時。請注意,這些基準測試是通過反射直接比較讀/寫屬性來完成的。在.NET中屬性是一對Get/Set方法,這是由編譯器生成的,但當屬性只包含一個簡單的內嵌字段時,.NET JIT會使用內聯Get/Set方法以提升性能。這意味著使用反射訪問屬性可能會遇到反射性能最差的情況,但它會被選擇是因為這是最常見的用例,數據位于 ORMs, Json序列化/反序列化庫和對象映射工具中。

    預約申請免費試聽課

    怕錢不夠?就業掙錢后再付學費!    怕學不會?從入學起,達內定制課程!     擔心就業?達內多家實踐企業供你挑選!

    上一篇:.NET Core如何實現RedisClient
    下一篇:.NET持續進化的統一開發平臺
    • 掃碼領取資料

      回復關鍵字:視頻資料

      免費領取 達內課程視頻學習資料

    • 視頻學習QQ群

      添加QQ群:1143617948

      免費領取達內課程視頻學習資料

    Copyright ? 2018 Tedu.cn All Rights Reserved 京ICP備08000853號-56 京公網安備 11010802029508號 達內時代科技集團有限公司 版權所有

    選擇城市和中心
    江西省

    貴州省

    廣西省

    海南省

    国拍自产亚洲 2019国拍自产在线,国拍自产亚洲,国产a在线不卡 百度 好搜 搜狗
    <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>