求助:CSharp程序内存资源释放问题

X.D.Washington 2020-09-11 08:22:40
语言:C#
平台:Windows
运行环境:.Net Framework
IDE:Visual Studio Community 2019

某赛车游戏使用config.ini文件作为软件配置文件,玩家在玩游戏过程中常常需要更改一些设置,其中包括更改/设置游戏帧率。大部分设置都可以在游戏设置界面内完成。关于帧数的设置,游戏界面并没有给出,然而这又是最重要的游戏参数之一,追求游戏性能和体验的玩家,包括职业玩家、主播、平民玩家都需要常常更改这一参数。

一般情况下,玩家修改帧数可以打开Windows文件资源管理器,然后找到游戏的配置文件目录,找到配置文件config.ini,用记事本打开,然后将其中的配置项比如“Framelimit=30”改成“Framelimit=38”,最后点击保存。

我自己也是该游戏玩家之一。因觉得这个过程需要多次点击,步骤稍显麻烦,就想写个小工具使用。写出来的程序能完美运行,然而打开任务管理器一看,更改ComboBox中的Text时候,程序占用的内存会上升。虽然占用的内存不大,只有几M,每次内存提高也只有几kB,但是强迫症的我还是想把内存攀升的原因弄明白。

整个程序的代码有数百行,为节省本文篇幅,以下仅贴出相关片段,如果还需要其他片段再在评论区贴出:

        private void ComboBox1_TextChanged(object sender, EventArgs e)
{
string ConfigFilePath = GetConfigFilePath(); // 获取玩家需要修改帧数的配置文件路径
if (File.Exists(ConfigFilePath))
{
try
{
pictureBox3.Visible = true; // 显示配置文件的图标,用户点击后可以用记事本改帧数

// 将配置文件中关于帧数的值放到TextBox内供用户修改
TextBoxesGetValues(ConfigFilePath);

// 读取配置文件中是否隐藏宠物的参数
string LoverPetShowFlag = ReadValueFromIniFile(Section, strLoverPetShowFlag, "null", ConfigFilePath);

if (LoverPetShowFlag == "0")
{
checkBox1.CheckState = CheckState.Checked; // 隐藏宠物
}
else
{
checkBox1.CheckState = CheckState.Unchecked; // 显示宠物
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
else
{
pictureBox3.Visible = false;
EmptyValueTextBoxes();
}
}

private void EmptyValueTextBoxes()
{
textBox1.Text = "";
textBox2.Text = "";
textBox3.Text = "";
}

private void TextBoxesGetValues(string iniFilePath)
{
textBox1.Text = ReadValueFromIniFile(Section, strFramelimit, "null", iniFilePath);
textBox2.Text = ReadValueFromIniFile(Section, strOutdoorFramelimit, "null", iniFilePath);
textBox3.Text = ReadValueFromIniFile(Section, strDanceFramelimit, "null", iniFilePath);
}

private string GetConfigFilePath()
{
StringBuilder sb = new StringBuilder();
sb.Append(QQSpeedDirectoryPath);
sb.Append(@"\");
sb.Append(comboBox1.Text);
sb.Append(@"\配置\config.ini");
return sb.ToString();
}


ComboBox中存放的是游戏账号,账号有多个的时候,可以用键盘上的上下箭头切换显示不同的账号。就是在这里,当改变ComboBox的Text时,程序占用的内存会上升,大约ComboBox.Text每改变5次,内存就增加1M。

是不是代码中缺少了资源释放相关的指令,还是现有代码写法不规范,还请大佬们解惑!本人是个编程小白,对C#的代码规范、运行机制、内存管理什么的不是很了解。上述代码中GetConfigFilePath()函数还是特意弄的,因为一开始是用“+”文本连接符获取配置文件路径的,后来网上查了说这样会导致String在内存中的复制,于是改用StringBuilder方法,没想到这样还是存在内存上升的问题。

在此向各位前辈们求助,望指点一二。跪谢!
...全文
10001 点赞 收藏 7
写回复
7 条回复
X.D.Washington 2020年09月15日
引用 5 楼 อาเหว่ย 的回复:
改用CONFIG 或者XML JSON都可以解决你的问题。 WIN32的东西。释放找不准基本都只能全关才会释放
感谢大佬指点!
回复 点赞
data-v-165b75c4 2020年09月14日
VS有性能诊断啊
回复 点赞
X.D.Washington 2020年09月14日
引用 4 楼 WQR1994 的回复:
VS有性能诊断啊
多谢提醒!我这就去试试
回复 点赞
อาเหว่ย 2020年09月14日
改用CONFIG 或者XML JSON都可以解决你的问题。 WIN32的东西。释放找不准基本都只能全关才会释放
回复 点赞
X.D.Washington 2020年09月12日
因为程序界面很小,取消显示最大化按钮后标题栏显得不好看,就选择了用无边框窗体,自己做标题栏。代码如下:
        // FormBorderStyle = None
        // 在Client Area上绘制文字和最小化、关闭按钮做标题栏
        // 为防止窗口控件闪烁,采用Graphic.DrawString的方式,取代Label控件
        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            SolidBrush drawBrush = new SolidBrush(Color.Black);

            e.Graphics.DrawString(TitleString, new Font("Microsoft YaHei", 9), drawBrush, 2, 5);
            e.Graphics.DrawLine(new Pen(Color.LightGray, 1), 0, 25, 300, 25);

            Font font = new Font("Verdana", 10);

            SizeF QQstringSize = e.Graphics.MeasureString("QQ", font);
            SizeF FramelimitstringSize = e.Graphics.MeasureString("Framelimit", font);
            SizeF OutdoorFramelimitstringSize = e.Graphics.MeasureString("OutdoorFramelimit", font);
            SizeF DanceFramelimitstringSize = e.Graphics.MeasureString("DanceFramelimit", font);

            int downoffset = 3;
            int dis = 1;
            int UpperLowerOffset = 3;
            e.Graphics.DrawString("QQ", font, drawBrush, comboBox1.Location.X - dis - QQstringSize.Width - 2, comboBox1.Location.Y + downoffset);
            e.Graphics.DrawString("Framelimit", font, drawBrush, textBox1.Location.X - dis - FramelimitstringSize.Width, textBox1.Location.Y + downoffset);
            e.Graphics.DrawString("OutdoorFramelimit", font, drawBrush, textBox2.Location.X - dis - OutdoorFramelimitstringSize.Width + UpperLowerOffset, textBox2.Location.Y + downoffset);
            e.Graphics.DrawString("DanceFramelimit", font, drawBrush, textBox3.Location.X - dis - DanceFramelimitstringSize.Width + UpperLowerOffset, textBox3.Location.Y + downoffset);

            drawBrush.Dispose();
            font.Dispose();
            // e.Graphics.Dispose();
        }
在这里本来想加入e.Graphics.Dispose();语句,因为我看到网上关于资源释放的示例代码有这句,但是写上之后就没法编译直接报错。 我昨晚又查了一些资料,网上又有说法说Paint事件不要轻易自己写。我在想会不会是本文开头提到的ComboBox的TextChanged事件触发了这里的 Form1_Paint事件,而Paint事件又没有进行e.Graphics.Dispose()导致的内存攀升。
回复 点赞
X.D.Washington 2020年09月12日
引用 1 楼 贵阳老马马善福专业维修游泳池堵漏防水工程 的回复:
ReadValueFromIniFile 怎么实现的,里面用到非托管内存或者实现了 IDispose 的对象,要释放。
程序中用到了非C#代码,即WIN32 API函数GetPrivateProfileString和WritePrivateProfileString:
        #region ini文件读取写入函数
        [DllImport("kernel32", CharSet = CharSet.Unicode)]
        private static extern long WritePrivateProfileString(string section, string key, string val, string filePath);
        [DllImport("kernel32", CharSet = CharSet.Unicode)]
        private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);
        public static string ReadValueFromIniFile(string Section, string Key, string def, string filePath)
        {
            StringBuilder temp = new StringBuilder(1024);
            int i = GetPrivateProfileString(Section, Key, def, temp, 1024, filePath);
            return temp.ToString();
        }
        public static void WriteValueToIniFile(string Section, string Key, string Value, string filePath)
        {
            WritePrivateProfileString(Section, Key, Value, filePath);
        }
        #endregion
至于这里该怎么释放,我试过这样的:
        public static string ReadValueFromIniFile(string Section, string Key, string def, string filePath)
        {
            StringBuilder temp = new StringBuilder(1024);
            int i = GetPrivateProfileString(Section, Key, def, temp, 1024, filePath);
            // temp.Dispose();
            return temp.ToString();
        }
但是这样写函数就没法返回string了。一开始做这程序的时候,就是因为对C/C++一无所知,要学Windows消息机制什么的,而且自己也没有那个能力写出安全的代码,在网上看到说写C#代码不需要自己对内存进行管理,就选择了C#。没想到后面做的时候发现很多关键地方还是得调用C/C++ API……
回复 点赞
ReadValueFromIniFile 怎么实现的,里面用到非托管内存或者实现了 IDispose 的对象,要释放。
回复 点赞
发动态
发帖子
C#
创建于2007-09-28

8.4w+

社区成员

64.0w+

社区内容

.NET技术 C#
社区公告
暂无公告