2010-02-04 49 views
8

我开发了一个我想发布的项目,它使用c#,WPF和System.Speech.Synthesizer对象。防止此项目发布的问题是,无论何时调用SpeakAsync,都会留下内存泄漏,从而导致最终失败。我相信在使用这个对象后我已经清理好了,但是找不到治疗方法。我已经通过蚂蚁内存分析器运行该程序,并且报告说每次调用WAVEHDR和WaveHeader都在增长。SpeechSynthesizer中的常量内存泄漏

我已经创建了一个示例项目来试图找出原因,但我仍然处于亏损状态。任何帮助,将不胜感激。

该项目使用VS2008,是一个面向.NET 3.5和任何CPU的c#WPF项目。您需要手动添加对System.Speech的引用。

这里是代码:

<Window x:Class="SpeechTest.Window1" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
Title="Window1" Height="300" Width="300"> 
<Grid> 
    <StackPanel Orientation="Vertical"> 

     <Button Content="Start Speaking" Click="Start_Click" Margin="10" /> 
     <Button Content="Stop Speaking" Click="Stop_Click" Margin="10" /> 
     <Button Content="Exit" Click="Exit_Click" Margin="10"/> 

    </StackPanel> 
</Grid> 



// Start of code behind 
using System; 
using System.Windows; 
using System.Speech.Synthesis; 

namespace SpeechTest 
{ 
    public partial class Window1 : Window 
    { 

     // speak setting 
     private bool speakingOn = false; 
     private int curLine = 0; 
     private string [] speakLines = { 
      "I am wondering", 
      "Why whenever Speech is called", 
      "A memory leak occurs", 
      "If you run this long enough", 
      "It will eventually crash", 
      "Any help would be appreciated" }; 

     public Window1() 
     { 
      InitializeComponent(); 
     } 

     private void Start_Click(object sender, RoutedEventArgs e) 
     { 
      speakingOn = true; 
      SpeakLine(); 
     } 

     private void Stop_Click(object sender, RoutedEventArgs e) 
     { 
      speakingOn = false; 
     } 

     private void Exit_Click(object sender, RoutedEventArgs e) 
     { 
      App.Current.Shutdown(); 
     } 

     private void SpeakLine() 
     { 
      if (speakingOn) 
      { 
       // Create our speak object 
       SpeechSynthesizer spk = new SpeechSynthesizer(); 
       spk.SpeakCompleted += new EventHandler(spk_Completed); 
       // Speak the line 
       spk.SpeakAsync(speakLines[curLine]); 
      } 
     } 

     public void spk_Completed(object sender, SpeakCompletedEventArgs e) 
     { 
      if (sender is SpeechSynthesizer) 
      { 

       // get access to our Speech object 
       SpeechSynthesizer spk = (SpeechSynthesizer)sender; 
       // Clean up after speaking (thinking the event handler is causing the memory leak) 
       spk.SpeakCompleted -= new EventHandler(spk_Completed); 
       // Dispose the speech object 
       spk.Dispose(); 
       // bump it 
       curLine++; 
       // check validity 
       if (curLine >= speakLines.Length) 
       { 
        // back to the beginning 
        curLine = 0; 
       } 
       // Speak line 
       SpeakLine(); 
      } 
     } 
    } 
} 




我在Windows上运行这个程序7 64位,它会运行,并试图创建一个新的SpeechSynthesizer对象时最终停止。在Windows Vista 64位上运行时,内存将从34k的起点增长到目前的大约400k,并在不断增长。

任何人都可以在代码中看到可能导致此问题的任何内容,或者这是Speech对象本身的问题。

任何帮助,将不胜感激。

+0

你确定它不断上升?内存将继续以.net的速度增长,直到GC通过并清理事件。除非它不断上升,永远不会下降,那么我不会担心它。 – 2010-02-04 23:37:28

+0

是的,当我在Windows 7上运行它时,它最终会在试图创建新的SpeechSynthesizer对象时停止。在程序暂停后,去控制面板并尝试测试文本到语音的结果会一样。在机器重新启动之前,它不会说话。 – DudeFX 2010-02-04 23:44:08

+0

如果您不在每次传递中创建新的SpeechSynthesizer对象,会发生什么情况? – 2010-02-19 20:50:58

回答

3

我可以证实这一观察。我拉我的头发试图找出我的程序在哪里泄漏,它是System.speech中的.SPEAK方法

我已经转换了一个使用基于COM的Speech对象的应用程序来使用新的System.Speech .Net 3.5中的.Net库。听起来就像在.NET应用程序中使用所有管理代码的正确方式。该应用程序突然有一个小的内存泄漏。

我将其分解为2个简单的应用程序,将“这是一个测试”转换为WAV文字的口头文字。一个使用基于COM的语音对象,另一个使用System.Speech。我跑了他们24小时,每个创造约200,000次的WAV。

基于COM的语音对象:没有内存泄漏。大约40分钟后,应用的内存使用达到了13MB

System.speech:缓慢泄漏,很好,线性。从大约14MB到45MB在24小时内

+0

谢谢!其实你的答案是唯一正确的答案! – JXITC 2012-05-22 18:37:23

+0

我也遇到了泄漏,现在通过您的提示解决了。 – JXITC 2012-05-22 18:37:46

2

SendAsync()Ping也泄漏。解决方法是首先将发件人作为IDisposable。所以也许下面的工作也在这里。

((IDisposable)spk).Dispose(); 
0

我可以给你一个非常简单的回答你的问题: 充分利用SpeechSynthesizer静态!

我很确定这会解决您的问题。

而且 - 小费== >>每次你的代码,你有资源......用它作为静态和你的生活会更好!

+0

我不认为实例是线程安全的,你会在多个线程中使用静态SpeechSynthesizer实例来解决问题。 – Nariman 2012-09-08 16:39:46