自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

記一次 .NET 某醫(yī)院預(yù)約平臺(tái) 內(nèi)存泄露分析

開(kāi)發(fā) 架構(gòu)
在我分析的真實(shí)dump案例中,見(jiàn)過(guò) Castle ProxyGenerator? 的泄露,也見(jiàn)過(guò) CodeAnalysis.CSharp.Scripting? 的泄露,還真沒(méi)見(jiàn)過(guò) XmlSerializer 的泄露,算是完美的補(bǔ)充了我的案例庫(kù)!

一:背景

1. 講故事

前幾天有位朋友找到我,說(shuō)他的程序有內(nèi)存泄露,讓我?guī)兔ε挪橐幌?,截圖如下:

圖片圖片

說(shuō)實(shí)話看到 32bit, 1.5G 這些關(guān)鍵詞之后,職業(yè)敏感告訴我,他這個(gè)可能是虛擬地址緊張所致,不管怎么說(shuō),有了 Dump 就可以上馬分析。

二:WinDbg分析

1. 虛擬地址緊張所致嗎

要看是不是虛擬地址緊張,可以用 !address -summary 觀察下內(nèi)存段統(tǒng)計(jì)信息,截圖如下:

圖片圖片

我去,用 WinDbg Preview 盡然分析不了,在加載 ntdll 的過(guò)程中死掉了,如果你是我們調(diào)試訓(xùn)練營(yíng)的朋友,應(yīng)該會(huì)深深的有體會(huì),我們分析的第一個(gè)dump就存在這個(gè)情況,這個(gè)加載不了其實(shí)就預(yù)示著一種非托管泄露,這里暫不劇透。

用 WinDbg Preview 分析不了怎么辦呢?可以用 Windbg 的其他版本哈,比如 Windbg10, WinDbg6 等等,這里就采用 WinDbg10 X86 版本打開(kāi)吧。

0:000> !address -summary

--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free                                    179          8cbb1000 (   2.199 GB)           54.97%
Heap                                   6598          376f6000 ( 886.961 MB)  48.09%   21.65%
<unknown>                              3091          31954000 ( 793.328 MB)  43.02%   19.37%
Image                                   376           8c0d000 ( 140.051 MB)   7.59%    3.42%
Stack                                    75           1780000 (  23.500 MB)   1.27%    0.57%
Other                                     7             4e000 ( 312.000 kB)   0.02%    0.01%
TEB                                      25             19000 ( 100.000 kB)   0.01%    0.00%
PEB                                       1              1000 (   4.000 kB)   0.00%    0.00%

--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE                                179          8cbb1000 (   2.199 GB)           54.97%
MEM_COMMIT                             9821          6bfad000 (   1.687 GB)  93.68%   42.18%
MEM_RESERVE                             352           7492000 ( 116.570 MB)   6.32%    2.85%

從卦中 MEM_COMMIT 的 %ofTotal= 42.18% 來(lái)看,提交內(nèi)存占總的虛擬地址比重還不到一半,這說(shuō)明我的猜測(cè)是錯(cuò)的,不存在虛擬地址緊張的情況,這里稍微提醒一下的是,這里不存在虛擬地址緊張是因?yàn)樗_(kāi)的是 Any CPU 模式,默認(rèn)能吃到 4G 內(nèi)存。

不管怎么說(shuō),現(xiàn)在被當(dāng)頭一棒,既然這條路走不通,那會(huì)是什么情況導(dǎo)致的呢?一般來(lái)說(shuō)這個(gè)內(nèi)存量我是不愿意分析的,但既然分析到這里也只能繼續(xù)分析,接下來(lái)用 !eeheap -gc 觀察下托管堆內(nèi)存占用情況。

0:000> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x777c0434
generation 1 starts at 0x77781000
generation 2 starts at 0x01861000
ephemeral segment allocation context: none
 segment     begin  allocated      size
01860000  01861000  0285ffdc  0xffefdc(16773084)
...
77780000  77781000  77aa25c0  0x3215c0(3282368)
Large object heap starts at 0x02861000
 segment     begin  allocated      size
02860000  02861000  031e5cc0  0x984cc0(9981120)
Total Size:              Size: 0x1f7e47e4 (528369636) bytes.
------------------------------
GC Heap Size:    Size: 0x1f7e47e4 (528369636) bytes.

從卦中看當(dāng)前托管堆也才 528M 和 提交內(nèi)存 1.6G 相距甚遠(yuǎn),所以這個(gè) dump 大概率是存在非托管內(nèi)存泄露,其實(shí) !address -summary 中的 Heap 也能佐證,說(shuō)到底就是 ntheap 泄露。

2. ntheap 怎么啦

深挖 ntheap 我就不挖了,省的誤入歧途,文章開(kāi)頭我說(shuō)過(guò) ntdll 無(wú)法加載的現(xiàn)象預(yù)示著一種非托管泄露,對(duì) ,就是 GC 的加載堆泄露,加載堆是 CLR 用來(lái)映射 C# 程序集,模塊,類(lèi)型,方法等用途的一塊私有內(nèi)存,那怎么去洞察它呢?可以使用 !eeheap -loader 命令洞察。

0:000> !eeheap -loader
Loader Heap:
--------------------------------------
...
Module 05829f78: Size: 0x0 (0) bytes.
Module 0582a8f8: Size: 0x0 (0) bytes.
Module 0582b278: Size: 0x0 (0) bytes.
Module 0582bbf8: Size: 0x0 (0) bytes.
Module 0582c578: Size: 0x0 (0) bytes.
Module 0582cef8: Size: 0x0 (0) bytes.
Module 0582d878: Size: 0x0 (0) bytes.
...
Module 362ea420: Size: 0x0 (0) bytes.
Total size:      Size: 0x0 (0) bytes.
--------------------------------------
Total LoaderHeap size:   Size: 0x7e7e000 (132636672) bytes total, 0x28000 (163840) bytes wasted.
=======================================

雖然加載堆只統(tǒng)計(jì)到了 132M,但其中的 module 高達(dá) 2.3w 個(gè),其實(shí)這里會(huì)有一些相關(guān)內(nèi)存是加載堆之外無(wú)法統(tǒng)計(jì)到的,一般正常的程序不可能有這么多的module,所以這就是我們接下來(lái)突破的點(diǎn),那怎么突破呢?最好的辦法就是觀察下這個(gè) module 中到底有什么 type,使用 !dumpmodule 命令即可。

0:000> !dumpmodule -mt 0582d878
Name:       Unknown Module
Attributes: Reflection 
Assembly:   0c229d38
LoaderHeap:              00000000
TypeDefToMethodTableMap: 050676e4
TypeRefToMethodTableMap: 050676f8
MethodDefToDescMap:      0506770c
FieldDefToDescMap:       05067734
MemberRefToDescMap:      00000000
FileReferencesMap:       05067784
AssemblyReferencesMap:   05067798

Types defined in this module

      MT  TypeDef Name
------------------------------------------------------------------------------
0582dcb0 0x02000002 
0582df90 0x02000003 
0582e018 0x02000004 
0582e0b8 0x02000005 
0582e194 0x02000006 

Types referenced in this module

      MT    TypeRef Name
------------------------------------------------------------------------------

從模塊中并沒(méi)有看到類(lèi)型的文字描述,那怎么辦呢,我們隨便抽一個(gè) mt 看下這個(gè) mt 下有什么方法,使用 !dumpmt 命令即可。

0:000> !dumpmt -md 0582dcb0
EEClass:         05068980
Module:          0582d878
Name:            
mdToken:         02000002
File:            Unknown Module
BaseSize:        0x44
ComponentSize:   0x0
Slots in VTable: 8
Number of IFaces in IFaceMap: 0
--------------------------------------
MethodDesc Table
   Entry MethodDe    JIT Name
739819c8 735e61fc PreJIT System.Object.ToString()
73987850 735e6204 PreJIT System.Object.Equals(System.Object)
7398bd80 735e6224 PreJIT System.Object.GetHashCode()
738ddbe8 735e6238 PreJIT System.Object.Finalize()
0583b529 0582dc8c   NONE Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterCallBack.InitCallbacks()
0583b52d 0582dc94   NONE Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterCallBack..ctor()
0583c7d0 0582dc74    JIT Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterCallBack.Write3_root(System.Object)
0583c868 0582dc80    JIT Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterCallBack.Write2_CallBack(System.String, System.String, xxx.Models.xxxBack, Boolean, Boolean)

看到卦中的這些信息,我相信有很多朋友知道是怎么回事了,對(duì),就是 Serialization 泄露,那它序列化什么類(lèi)型呢 ? 從卦中看就是 xxx.Models.xxxBack 類(lèi),即 xmlSerializer.Serialize(xxx.Models.xxxBack) 的相關(guān)邏輯,接下來(lái)就需要逆向看下到底是哪里寫(xiě)的,結(jié)果發(fā)現(xiàn)是他的底層庫(kù)封裝的,有些方法有問(wèn)題,有些沒(méi)問(wèn)題,真的是無(wú)語(yǔ)哈。

//有問(wèn)題的方法
    public static string Serialize(object o, Encoding encoding, string rootName)
    {
        XmlSerializer xmlSerializer = new XmlSerializer(o.GetType(), new XmlRootAttribute(rootName));
        ...
        xmlSerializer.Serialize(memoryStream, o, xmlSerializerNamespaces);
        return encoding.GetString(memoryStream.ToArray());
    }

    //正確的方法
    public static string Serialize(object Obj, Encoding encoding)
    {
        ...
        using (XmlWriter xmlWriter = XmlWriter.Create(memoryStream, xmlWriterSettings))
        {
            XmlSerializerNamespaces xmlSerializerNamespaces = new XmlSerializerNamespaces();
            xmlSerializerNamespaces.Add("", "");
            new XmlSerializer(Obj.GetType()).Serialize(xmlWriter, Obj, xmlSerializerNamespaces);
        }
        return encoding.GetString(memoryStream.ToArray());
    }

這是一個(gè)老生常談的問(wèn)題,如果你用 new XmlSerializer(o.GetType(), new XmlRootAttribute(rootName)); 模式的話,一定要緩存起來(lái),否則就會(huì)泄露,只能說(shuō)是微軟造的一個(gè)大坑吧,多少人都踩上去了。

三:總結(jié)

在我分析的真實(shí)dump案例中,見(jiàn)過(guò) Castle ProxyGenerator 的泄露,也見(jiàn)過(guò) CodeAnalysis.CSharp.Scripting 的泄露,還真沒(méi)見(jiàn)過(guò) XmlSerializer 的泄露,算是完美的補(bǔ)充了我的案例庫(kù)!

責(zé)任編輯:武曉燕 來(lái)源: 一線碼農(nóng)聊技術(shù)
相關(guān)推薦

2023-05-15 11:15:50

.NET門(mén)診語(yǔ)句

2023-10-07 13:28:53

.NET軟件賬本

2023-06-26 00:12:46

2021-11-02 07:54:41

內(nèi)存.NET 系統(tǒng)

2023-09-26 01:11:58

MES非托管泄露

2022-10-24 07:48:37

.NETCPUGC

2024-12-27 13:31:18

.NETdump調(diào)試

2024-03-28 12:56:36

2023-04-06 10:52:18

2021-10-09 10:24:08

NET爬蟲(chóng)內(nèi)存

2022-10-13 18:40:05

.NETOA后端

2023-03-26 20:24:50

ERP網(wǎng)站系統(tǒng)

2023-09-27 07:23:10

.NET監(jiān)控軟件

2024-03-26 00:44:53

.NETCIM系統(tǒng)

2024-07-12 11:20:34

.NET崩潰視覺(jué)程序

2024-05-20 09:39:02

.NETurl線程池

2024-07-01 13:00:24

.NET網(wǎng)絡(luò)邊緣計(jì)算

2021-10-27 07:30:32

.NETCPU論壇

2022-01-17 21:28:36

管理系統(tǒng).NET

2022-10-25 14:17:01

.NET代碼程序
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)