C#中的内存问题使用while(true)

时间:2014-11-11 13:12:35

标签: c#

我想在C#中编写一个客户端,用于检查用户是否登录了不同的客户端。客户端应该全天候运行并刷新数据库,并为每个客户端提供一些状态信息。

我的问题是:命令行工具需要越来越多的内存,所以我觉得有一个问题是我分配永远不会释放的内存。

我认为我正在创建一个ManagementScope,但我不能为它设置Dispose()方法。

这是我的代码:

static void Main(string[] args)
    {

        Ping pingSender = new Ping();
        PingOptions options = new PingOptions();

        string sqlconnectionstring = "Data Source=(local)\\SQLEXPRESS;Initial Catalog=clientstat;User ID=...;Password=....;Integrated Security=SSPI";
        SqlConnection clientread = new SqlConnection(sqlconnectionstring);
        clientread.Open();

        // Use the default Ttl value which is 128,
        // but change the fragmentation behavior.
        options.DontFragment = true;

        string username = "";

        // Create a buffer of 32 bytes of data to be transmitted.
        string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
        byte[] buffer = Encoding.ASCII.GetBytes(data);
        int timeout = 120;

        while (true)
        {

            SqlCommand clientcommand = new SqlCommand("SELECT * FROM Client WHERE StateID = @stateid", clientread);
            clientcommand.Parameters.Add(new SqlParameter("stateid", 1));

            SqlDataReader clientreader = clientcommand.ExecuteReader();
            while (clientreader.Read())
            {
                string ipadress = Convert.ToString(clientreader["IP"]);
                string clientid = Convert.ToString(clientreader["ID"]);

                if (ipadress != string.Empty && clientid != string.Empty)
                {
                    // First Try To Ping Computer

                    PingReply reply = pingSender.Send(ipadress, timeout, buffer, options);
                    if (reply.Status == IPStatus.Success)
                    {

                        try
                        {

                            ManagementScope managementScope = new ManagementScope((@"\\" + ipadress + @"\root\cimv2"));
                            managementScope.Options.Username = "....";
                            managementScope.Options.Password = "...";
                            managementScope.Options.EnablePrivileges = true;
                            // ObjectQuery to Check if User is logged on
                            ObjectQuery objectQuery = new ObjectQuery("SELECT * FROM Win32_ComputerSystem");
                            ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher(managementScope, objectQuery);
                            ManagementObjectCollection querycollection = managementObjectSearcher.Get();

                            foreach (ManagementObject mo in querycollection)
                            {
                                // Check Here UserName
                                username = Convert.ToString(mo["UserName"]);
                                if (username != "")
                                {
                                    Console.WriteLine(ipadress + " " + username);

                                }

                            }
                            querycollection.Dispose();
                            managementObjectSearcher.Dispose();
                        }
                        catch (Exception x)
                        {
                            Console.WriteLine(x.Message);
                        }
                    }
                }
                else
                {
                    Console.WriteLine(clientid + " has no IP-Adress in Database");
                }
            }
            clientcommand.Dispose();
            clientreader.Close();
            clientreader.Dispose();
        }

    }

}

任何想法或建议我可以在这里改进什么?或者究竟什么是问题?

提前致谢

5 个答案:

答案 0 :(得分:2)

创意1:

您必须处理ManagementObject以释放非托管COM资源。

不幸的是,there is a bug in the Dispose实现了它。 Here are more details关于它。

积分应转到使用GC.Collect()提供解决方法的this answer。不幸的是,它的成本。

这就是为什么最好使用计数器来执行每{n}个循环GC.Collect,使用n值你将手动调整,直到表演可以接受为止。

无论如何,我会尝试使用反射来调用ManagementObject Dispose()。

创意2:

通常,为多个查询重用已打开的连接并不好,因为它会阻止连接池机制以最佳方式工作。因此,如果使用的话,sqlconnection可能会保留资源。

相反,请在循环中包含SqlConnection create / open和close / dispose,与this question相关。

答案 1 :(得分:1)

您应该使用using(而不是调用Dispose(),不需要它)。 "新"问题是嵌套,看起来像这样:

using (SqlConnection ...) 
{
    using (SqlCommand ...)
    {
        using (SqlDataReader ...) 
        {
           ...
        }
    }
}

基本上,如果你正在实现实现IDisposable的东西,请在那里放一个using并确保.NET会为你处理内存(至少,它会尝试到)。

答案 2 :(得分:0)

尝试在每次顶级迭代后添加GC.Collect()调用(仅用于诊断问题)。查看内存是否表现相同。如果没有,那么你没有问题,GC可能只是乐观并延迟收集。

由于数据读取器缓冲区的原因,每次迭代都会使用一些非常重要的空间,如果没有收集这些空间,那么你会看到内存只会增加。

但这只是一个误报。如果您的系统变得内存受限或应用程序触发某种内部GC阈值收集就会发生。

答案 3 :(得分:0)

我不确定你为什么要在每次循环迭代中创建一个新的SqlCommand

只需参数化SqlCommand,并在循环迭代中设置参数,而不是创建新的SqlCommand

这样做,让我知道记忆的样子。还记得一件事 - 在启动之前GC不会启动(即非确定性规则生效)。除非你真的想要在你的循环中运行GC.Collect(这绝对是疯狂,恕我直言)。换句话说,对象的不断创建/处理可能会使内存增长。请记住,天真的Dispose不会让记忆神奇地缩小。还要记住.NET的内存管理模型,你应该没问题。

答案 4 :(得分:0)

您获得此异常的原因是您正在使用while(true)语句并且您没有使用break;在循环中的任何地方明确地打破它。因此while循环在无限次执行,因此占用了大量内存。我认为你应该使用Windows服务而不是while(true)来全天候运行它并毫无例外地进行操作。