• 020-34031965
  • geek18@gmail.com

WPF的TextBox产生内存泄露的情况

WPF的TextBox产生内存泄露的情况

前段时间参与了一个WPF编写的项目,在该项目中有这样一个场景:在程序运行过程中需要动态地产生大量文本信息,并追加WPF界面上的一个TextBox的Text中进行显示。编写完之后,运行该项目的程序,发现在产生大量信息之后,发现系统变慢了,打开任务管理器才发现,该项目的程序占用了将近1.5G的内存(天啊!!!这不是一般的耗内存啊!!!)。后来通过查资料和探索才发现了WPF的TextBox在追加Text显示文本时会造成内存泄露。下面通过一个小Demo程序来展示一下这个内存泄露。

我的Demo程序很简单,就是在界面上显示一个TextBox和一个Button,点击Button后就从0到9999进行for循环并将这些数字追加的TextBox的Text中进行显示。代码如下,

<window x:Class="TextBoxMemoryLeak.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="测试TextBox内存泄露" Height="350" Width="525"
        WindowStartupLocation="CenterScreen">
    <grid Margin="5">
        </grid><grid .RowDefinitions>
            <rowdefinition Height="*"></rowdefinition>
            <rowdefinition Height="35"></rowdefinition>
        </grid>
        <dockpanel Grid.Row="0">
            <textbox Name="tbOutput" IsReadOnly="True" VerticalScrollBarVisibility="Auto"></textbox>
        </dockpanel>
        <stackpanel Grid.Row="1"
                    FlowDirection="RightToLeft"
                    Orientation="Horizontal">
            <button Name="btnStart" Content="开 始" Margin="5,4,5,4" Width="65" Click="btnStart_Click"></button>
        </stackpanel>
    
</window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace TextBoxMemoryLeak
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void btnStart_Click(object sender, RoutedEventArgs e)
        {
            this.btnStart.IsEnabled = false;
            this.tbOutput.Text = "";

            for (int i = 0; i < 10000; i++)
            {
                //使用此语句进行Textbox的追加会造成内存泄露
                //this.tbOutput.Text += string.Format("{0}\n", i);

                //使用此语句进行Textbox的追加可避免内存泄露
                this.tbOutput.AppendText(string.Format("{0}\n", i));
            }

            this.btnStart.IsEnabled = true;
        }
    }
}

界面如下所示:

内存泄露情况

最初我们采用的是TextBox的Text追加方式如下

this.tbOutput.Text += string.Format("{0}\n", i);

构建,启动调试后,我们查看任务管理器,此时所占内存只有16M,

点击【开始】按钮之后,等到从0输出到9999之后,我们再查看任务管理器,发现此时所占的内存飙到了600+M,

若此时再点击【开始】按钮,等循环结束,发现所占内存飙到了900+M,

再点击【开始】按钮的话,就要发生OutOfMemory异常的。当我们将循环改为从0到19999时,第一次点击【开始】按钮,我的机器就发生OutOfMemory异常了。

避免内存泄露的情况

将TextBox的Text追加方式改为下面语句

this.tbOutput.AppendText(string.Format("{0}\n", i));

构建,启动调试,然后点击界面的【开始】按钮,等循环结束,我们查看任务管理器,测试Demo程序只占了29M内存(此时是从0到19999的循环)。

 

 

 

TextBox存在内存泄露的可能

背景

-WPF桌面程序中,增加了一个TextBox控件,用于显示输出的日志信息,日志信息量很大(具体数值未统计)

XAML 代码
 <TextBox Text="{Binding Message}" TextWrapping="Wrap" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" TextChanged="TextBox_TextChanged" />

CS 代码
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        if (!(sender is TextBox)) return;
        var tb = (TextBox)sender;
        if (tb.Text.Length > 30000) //textbox maxlength = 32767
      {
        tb.Text = "Auto Clear Histroy Information\n\r";
      }
       tb.Select(tb.Text.Length, 0);
    }

问题

运行一段时间后操作系统弹出信息,称“XXX程序出问题了”(截图后补吧)。在增加日志显示功能前,此异常未出现。

解决

参考:Google搜索到的信息
怀疑是日志信息太多,造成程序内存泄露。TextBox控件支持回退功能(Undo),但是回退功能需要占用更多内存。

设置 
     UndoLimit="0"  或者 IsUndoEnabled="False"
 可关闭Undo功能
thedarkside

你需要登陆后才能评论。