<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      happyhippy

      這個世界的問題在于聰明人充滿疑惑,而傻子們堅信不疑。--羅素

          .Net Framework 3.0帶了個System.Speech.dll,裝個語音包,然后就可以實現文字朗讀等功能。最近在使用的時候,發現隨著程序的運行,程序占用的內存一直在增長,直到程序崩潰。

           用WinDbg抓了個Dump,然后看了下,里面一堆沒有釋放的SPVTEXTFRAG、AudioDeviceOut+InItem、WAVEHDR、WaveHeader對象,于是寫了一小段來測試:

         1: //happyhippy.cnblogs.com
         2: using System.Speech.Synthesis;
         3: using (SpeechSynthesizer ss = new SpeechSynthesizer())
         4: {
         5:     for (int i = 0; i < int.MaxValue; i++)
         6:     {
         7:         ss.Speak(i.ToString());
         8:     }
         9: }

          跑了幾個小時,然后抓了個內存,開始干活:

       

      1. 看下內存中有哪些對象

         1: 0:000> .load clr20/sos.dll
         2: 0:000> !dumpheap -stat
         3: PDB symbol for mscorwks.dll not loaded
         4: total 275193 objects
         5: Statistics:
         6:       MT    Count    TotalSize Class Name
         7: 70441ff8        1           12 System.RuntimeTypeHandle
         8: 7043fc7c        1           12 System.__Filters
         9: //。。。。。。。。。。。。中間省略了一堆。。。。。。。。。。。
        10: 704431a8       18         1008 System.Collections.Hashtable
        11: 7042c938       65         1560 System.Security.Policy.StrongNameMembershipCondition
        12: 70441cd4       81         1620 System.RuntimeType
        13: 70441784       16         1820 System.Char[]
        14: 7042c6e4      232         6496 System.Security.SecurityElement
        15: 70442b84      278         6672 System.Collections.ArrayList
        16: 704435c4       17        75056 System.Byte[]
        17: 5610529c     4297        85940 System.Speech.Internal.AlphabetConverter+PhoneMapData+ConversionUnit
        18: 704432a4       18       181896 System.Collections.Hashtable+bucket[]
        19: 70414324      376       319064 System.Object[]
        20: 70440b54    13185       396696 System.String
        21: 5610ab38     7538       693496 System.Speech.Synthesis.TtsEngine.SPVTEXTFRAG
        22: 56105c50    63264      1012224 System.Speech.Internal.Synthesis.AudioDeviceOut+InItem
        23: 560f2694    65652      2626080 System.Speech.Internal.Synthesis.WAVEHDR
        24: 56105b6c    63264      3289728 System.Speech.Internal.Synthesis.WaveHeader
        25: 002be948    56465    172964660      Free
        26: Total 275193 objects
        27: Fragmented blocks larger than 0.5 MB:
        28:     Addr     Size      Followed by
        29: 1228c6d8    0.5MB         12311d7c System.Speech.Synthesis.TtsEngine.SPVTEXTFRAG
        30: 123293e0    0.6MB         123baa64 System.Speech.Synthesis.TtsEngine.SPVTEXTFRAG
        31: 124be36c    1.9MB         1269d898 System.Speech.Synthesis.TtsEngine.SPVTEXTFRAG
        32: 126a39f0    1.3MB         127e6644 System.Speech.Synthesis.TtsEngine.SPVTEXTFRAG

       

          列表中的第25行,172M的Free狀態的內存。

          最后4行(29~32)的內存碎片,用DumpObj看了下,都是Free狀態。

       

      2. 看看終結隊列中有哪些對象

         1: 0:000> !FinalizeQueue -detail
         2: SyncBlocks to be cleaned up: 0
         3: MTA Interfaces to be released: 0
         4: STA Interfaces to be released: 0
         5: ----------------------------------
         6: generation 0 has 2973 finalizable objects (068fdec4->06900d38)
         7: generation 1 has 11 finalizable objects (068fde98->068fdec4)
         8: generation 2 has 60304 finalizable objects (068c3058->068fde98)
         9: Ready for finalization 0 objects (06900d38->06900d38)
        10: Statistics:
        11:       MT    Count    TotalSize Class Name
        12: 7043a304        1           16 System.WeakReference
        13: 7002d1c8        1           20 System.ComponentModel.AsyncOperation
        14: 5610a9c0        1           20 System.Speech.Synthesis.SpeechSynthesizer
        15: 5610576c        1           20 System.Speech.Internal.ObjectTokens.RegistryDataKey
        16: 5610581c        1           24 System.Speech.Internal.ObjectTokens.ObjectToken
        17: 56105ae8        1           56 System.Speech.Internal.Synthesis.AudioDeviceOut
        18: 70427b90        4           80 Microsoft.Win32.SafeHandles.SafeWaitHandle
        19: 561092e0        1          176 System.Speech.Internal.Synthesis.VoiceSynthesis
        20: 7043b370        9          180 Microsoft.Win32.SafeHandles.SafeRegistryHandle
        21: 70441128        4          224 System.Threading.Thread
        22: 56105b6c    63264      3289728 System.Speech.Internal.Synthesis.WaveHeader
        23: Total 63288 objects

          我靠,內存中3289728個WaveHeader全部掛在終結隊列中。一個可能的原因就是:WaveHeader對象中實現了析構函數,并且程序使用完WaveHeader對象后,沒有手動釋放(Dispose),而是等著GC來自動回收。

       

      3. 用Reflector看WaveHeader的源代碼

         1: internal sealed class WaveHeader : IDisposable
         2: {
         3:     // Fields
         4:     internal int _dwBufferLength;
         5:     private GCHandle _gcHandle = new GCHandle();
         6:     private GCHandle _gcHandleWaveHdr = new GCHandle();
         7:     private WAVEHDR _waveHdr = new WAVEHDR();
         8:     internal const int WAVE_FORMAT_PCM = 1;
         9:     internal const int WHDR_BEGINLOOP = 4;
        10:     internal const int WHDR_DONE = 1;
        11:     internal const int WHDR_ENDLOOP = 8;
        12:     internal const int WHDR_INQUEUE = 0x10;
        13:     internal const int WHDR_PREPARED = 2;
        14:  
        15:     // Methods
        16:     internal WaveHeader(byte[] buffer)
        17:     {
        18:         this._dwBufferLength = buffer.Length;
        19:         this._gcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);//申請內存
        20:     }
        21:  
        22:     public void Dispose()
        23:     {
        24:         this.Dispose(true);
        25:         GC.SuppressFinalize(this);
        26:     }
        27:  
        28:     private void Dispose(bool disposing)
        29:     {
        30:         if (disposing)
        31:         {
        32:             this.ReleaseData();
        33:             if (this._gcHandleWaveHdr.IsAllocated)
        34:             {
        35:                 this._gcHandleWaveHdr.Free();
        36:             }
        37:         }
        38:     }
        39:  
        40:     ~WaveHeader()
        41:     {
        42:         this.Dispose(false);
        43:     }
        44:  
        45:     internal void ReleaseData()
        46:     {
        47:         if (this._gcHandle.IsAllocated)
        48:         {
        49:             this._gcHandle.Free();
        50:         }
        51:     }
        52:  
        53:     // Properties
        54:     internal int SizeHDR
        55:     {
        56:         get
        57:         {
        58:             return Marshal.SizeOf(this._waveHdr);
        59:         }
        60:     }
        61:  
        62:     internal GCHandle WAVEHDR
        63:     {
        64:         get
        65:         {
        66:             if (!this._gcHandleWaveHdr.IsAllocated)
        67:             {
        68:                 this._waveHdr.lpData = this._gcHandle.AddrOfPinnedObject();
        69:                 this._waveHdr.dwBufferLength = (uint) this._dwBufferLength;
        70:                 this._waveHdr.dwBytesRecorded = 0;
        71:                 this._waveHdr.dwUser = 0;
        72:                 this._waveHdr.dwFlags = 0;
        73:                 this._waveHdr.dwLoops = 0;
        74:                 this._waveHdr.lpNext = IntPtr.Zero;
        75:                 this._gcHandleWaveHdr = GCHandle.Alloc(this._waveHdr, GCHandleType.Pinned);//申請內存
        76:             }
        77:             return this._gcHandleWaveHdr;
        78:         }
        79:     }
        80: }

         如我們所料,的確定義了一個析構函數(~WaveHeader()),WaveHealder實現了Dispose模式。

       

      4.  進一步分析,看看WaveHeader都申請了啥資源,為啥要實現Dispose模式

          WaveHeader的構造函數中,申請了一片Pinned狀態的內存this._gcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned),并且其WAVEHDR屬性的實現中,也通過延遲加載的方式申請了一片Pinned狀態的內存this._gcHandleWaveHdr = GCHandle.Alloc(this._waveHdr, GCHandleType.Pinned)。

          MSDN對GCHander的闡述如下:(FROM MSDN)

      GCHandle 類與 GCHandleType 枚舉結合使用以創建對應于任何托管對象的句柄。此句柄可為以下四種類型之一:Weak、WeakTrackResurrection、Normal 或 Pinned。分配了句柄以后,在非托管客戶端保留唯一的引用時,可以使用它防止垃圾回收器回收托管對象。如果沒有這樣的句柄,則在該對象代表非托管客戶端完成工作以前,有可能被垃圾回收器回收。 

      用 GCHandle 創建一個固定對象,該對象返回??個內存地址,并防止垃圾回收器在內存中移動該對象。

      超出范圍時,您必須通過調用Free方法顯式釋放它;否則,可能會發生內存泄漏。當您釋放固定的句柄時,如果沒有對關聯對象的其他引用,則關聯對象將解除固定,并可以被當成垃圾回收。

          也就是說,通過GCHandler.Alloc申請的內存,必須通過調用GCHandler.Free方法來手動釋放。表面上看來,WaveHeader的實現中,的確有手動釋放這些申請的內存。

       

       

       

       

       

      5.  WaveHeader在哪兒使用

         SpeechSynthesizer封裝了一對東西,有點兒繞,我順著Speak方法找了N久,沒有找到使用WaveHeader的地方。

         只好換另一個思路:在上面的第1步中,我們可以看到,WaveHeader的MT Address為56105b6c,于是:

      0:000> !dumpheap -MT 56105b6c
      //。。。省卻6萬多行。。。  
      127fabdc 56105b6c       52     
      12800a64 56105b6c       52     //找到一個還活著的對象
      total 63264 objects
      Statistics:
            MT    Count    TotalSize Class Name
      56105b6c    63264      3289728 System.Speech.Internal.Synthesis.WaveHeader
      Total 63264 objects

         然后找到一個還活著的對象(地址為12800a64),看看它被誰引用了:

      0:000> !gcroot 12800a64
      Note: Roots found on stacks may be false positives. Run "!help gcroot" for
      more info.
      Scan Thread 0 OSTHread 3668
      ESP:1df238:Root:01f67e1c(System.Speech.Internal.Synthesis.VoiceSynthesis)->
      01f6841c(System.Speech.Internal.Synthesis.AudioDeviceOut)->
      01f68570(System.Collections.Generic.List`1[[System.Speech.Internal.Synthesis.AudioDeviceOut+InItem, System.Speech]])->
      02f91c10(System.Object[])->
      020e4088(System.Speech.Internal.Synthesis.AudioDeviceOut+InItem)->
      12800a64(System.Speech.Internal.Synthesis.WaveHeader)
      Scan Thread 2 OSTHread 2c5c
      Scan Thread 3 OSTHread 1160
      Scan Thread 4 OSTHread 211c
      Scan Thread 6 OSTHread 22d4
      Scan Thread 7 OSTHread 2fcc
      Scan Thread 8 OSTHread 377c
         這里,我們終于可以看到路徑了:

       

      SpeechSynthesizer
      ->VoiceSynthesis
      ->AudioDeviceOut
      -> List<AudioDeviceOut + InItem>
      -> AudioDeviceOut + InItem(內部類)
      -> WaveHeader
          終于找到目標了:AudioDeviceOut。

       

      6.  怎么使用WaveHeader

          AudioDeviceOut的代碼頁有點長,這里只截出部分使用WaveHeader的代碼:

         1: internal override void Play(byte[] buffer)
         2: {
         3:     if (this._deviceOpen)
         4:     {
         5:         int length = buffer.Length;
         6:         this._bytesWritten += length;
         7:         WaveHeader waveHeader = new WaveHeader(buffer);//上面第3節提到,這里通過GCHandler.Alloc申請了內存
         8:         GCHandle wAVEHDR = waveHeader.WAVEHDR;//上面第3節提到,這里通過GCHandler.Alloc申請了內存
         9:         MMSYSERR errorCode = SafeNativeMethods.waveOutPrepareHeader(this._hwo, wAVEHDR.AddrOfPinnedObject(), waveHeader.SizeHDR);
        10:         if (errorCode != MMSYSERR.NOERROR)
        11:         {
        12:             throw new AudioException(errorCode);
        13:         }
        14:         lock (this._noWriteOutLock)
        15:         {
        16:             if (!base._aborted)
        17:             {
        18:                 lock (this._queueIn)
        19:                 {
        20:                     InItem item = new InItem(waveHeader);//InItem封裝了一下WaveHeader,也沒啥
        21:                     this._queueIn.Add(item); //將Item加入到List中
        22:                     this._evt.Reset();
        23:                 }
        24:                 errorCode = SafeNativeMethods.waveOutWrite(this._hwo, wAVEHDR.AddrOfPinnedObject(), waveHeader.SizeHDR);//P/Invoke向設備輸出數據
        25:                 if (errorCode != MMSYSERR.NOERROR)
        26:                 {
        27:                     lock (this._queueIn)
        28:                     {
        29:                         this._queueIn.RemoveAt(this._queueIn.Count - 1);//如果出錯了,從List刪除一個Item
        30:                         throw new AudioException(errorCode);
        31:                     }
        32:                 }
        33:             }
        34:         }
        35:     }
        36: }

         在這里,Play方法了面,終于實例化了WaveHeader和WaveHDR(GCHandler);但是實例化的WaveHeader又被InItem封裝了一下,加入到List<InItem> _queueIn中。
          Play方法里面創建了WaveHeader,但是卻沒有看到釋放WaveHeader的代碼。

          那我們再來看下,其他地方有沒有釋放WaveHeader的代碼:既然InItem/WaveHeader被加入到_queueIn中了,程序應該還要使用WaveHeader;那么當InItem從該_queueIn中移出的時候,應該就會釋放WaveHeader了吧。順著這個思路繼續往下看。。。

         很不幸,就在Play方法里面:當判斷播放出錯的時候,會把InItem從_queueIn中移出(上面第29行),但是卻沒有釋放InItem/WaveHeader。

        另一個將InItem從_queueIn中移出的地方是CallBackProc,AudioDeviceOut在構造函數中初始化了一個委托:this._delegate = new SafeNativeMethods.WaveOutProc(this.CallBackProc); 從字面上理解,應該是輸出聲音完畢后,調用這個回調函數,來釋放內存:

         1: private void CallBackProc(IntPtr hwo, MM_MSG uMsg, IntPtr dwInstance, IntPtr dwParam1, IntPtr dwParam2)
         2: {
         3:     if (uMsg == MM_MSG.MM_WOM_DONE)
         4:     {
         5:         lock (this._queueIn)
         6:         {
         7:             InItem item = this._queueIn[0];
         8:             item.ReleaseData();//釋放InItem/WaveHeader
         9:             this._queueIn.RemoveAt(0); //移除InItem
        10:             this._queueOut.Add(item);
        11:             while (this._queueIn.Count > 0)
        12:             {
        13:                 item = this._queueIn[0];
        14:                 if (item._waveHeader != null)
        15:                 {
        16:                     break;
        17:                 }
        18:                 if ((this._asyncDispatch != null) && !base._aborted)
        19:                 {
        20:                     this._asyncDispatch.Post(item._userData);
        21:                 }
        22:                 this._queueIn.RemoveAt(0);//移除InItem,但是卻沒有釋放InItem/WaveHeader
        23:             }
        24:         }
        25:         if (this._queueIn.Count == 0)
        26:         {
        27:             this._evt.Set();
        28:         }
        29:     }
        30: }

          代碼中,可以看到,當uMsg == MM_MSG.MM_WOM_DONE,才會執行釋放操作。MM_MSG枚舉有三個可選值:MM_WOM_CLOSE = 0x3bc, MM_WOM_DONE = 0x3bd, MM_WOM_OPEN = 0x3bb,暫時不太清楚是回調函數的uMsg參數 否可能會傳入其他值;如果傳入了其他值,顯然InItem/WaveHeader有沒有被釋放。

          即使uMsg == MM_MSG.MM_WOM_DONE,我們來看下釋放的代碼,上面第8~9行,從_queueIn中移除InItem/WaveHeader的時候,的確執行了ReleaseData()來釋放內存,但是看該方法的源代碼:

         1: //happyhippy.cnblogs.com
         2: //InItem
         3: internal void ReleaseData()
         4: {
         5:     if (this._waveHeader != null)
         6:     {
         7:         this._waveHeader.ReleaseData();
         8:     }
         9: }
        10: //WaveHeader
        11: internal void ReleaseData()
        12: {
        13:     if (this._gcHandle.IsAllocated)
        14:     {
        15:         this._gcHandle.Free();
        16:     }
        17: }
        18:  

         里面調用WaveHeader.ReleaseData()來釋放內存,不是調用Dispose來釋放內存。上面提到,Play方法中,第8行GCHandle wAVEHDR = waveHeader.WAVEHDR申請了內存,但是ReleaseData()方法并沒有釋放該內存。應該調用Dispose來釋放內存,而不是調用ReleaseData()。

         然后在回過頭來看CallBackProc方法,從源代碼中可以看到,其只釋放了_queueIn中第一個InItem/WaveHeader,后續的InItem/WaveHeader從_queueIn中移除的時候,并沒有釋放內存。

        GCHandler.Alloc申請的內存,必須通過調用Free方法顯式釋放它;否則,可能會發生內存泄漏。

       

      7.  解決辦法

         無解。

         那封裝的一大坨,都是Internal。

      posted on 2010-11-07 20:52  Silent Void  閱讀(4658)  評論(14)    收藏  舉報

      主站蜘蛛池模板: 在熟睡夫面前侵犯我在线播放| 国产一区二区波多野结衣| 中文有无人妻vs无码人妻激烈| 国产精品一区在线蜜臀| 亚洲日韩久热中文字幕| 免费国产一级 片内射老| 国产高潮刺激叫喊视频| 白白发布视频一区二区视频| 亚洲综合色婷婷中文字幕| 99在线小视频| 精品国精品无码自拍自在线| 国产免费午夜福利在线观看| 蜜臀av久久国产午夜| 中文字幕无码av不卡一区| 国产精品久久久久久福利69堂| 欧美国产精品不卡在线观看| 成人网站国产在线视频内射视频| 亚洲国产日韩精品一区二区三区| 美日韩不卡一区二区三区| 中文字幕国产在线精品| 日本亚洲一区二区精品| 日韩中文字幕在线不卡一区| 亚洲第一极品精品无码久久| 国产一区二区在线影院| 国内自拍偷拍福利视频看看| 国产乱子伦一区二区三区视频播放| 亚洲国产高清av网站| 国产360激情盗摄全集| 在线A毛片免费视频观看| 日韩视频中文字幕精品偷拍| 熟女激情乱亚洲国产一区| 隆德县| 国产一区二区三区我不卡| 亚洲一区二区精品极品| 亚洲熟女片嫩草影院| 自拍偷自拍亚洲一区二区| 国产精品亚洲mnbav网站| 激情综合网一区二区三区| 人妻日韩精品中文字幕| 午夜毛片不卡免费观看视频| 国内免费视频成人精品|