.net 中异步SOCKET发送数据时碰到的内存问题 (二)

接昨天的文章

基本上我们可以认为发送数据的那一部分中有问题:每一次发送,有两个NEW的动作,产生两个新的对象。现在问题的关键是:为什么这两个新的对象没办法被回收!我们先做一实验,代码如下:
[csharp] view plain copy

namespace Test2  
{  
    class Program  
    {  
        static void Main(string[] args)  
        {  
            Console.WriteLine("Press any key to start.");  
            Console.ReadKey();  

            for (int i = 0; i < 1000000; ++i)  
            {  
                for (int j = 0; j < 10000; ++j)  
                {  
                    SocketAsyncEventArgs e = new SocketAsyncEventArgs();  
                }  
                System.Threading.Thread.Sleep(1000);  
            }  
        }  
    }  
}  

观察了几次,每次5分钟左右,内存波动范围:

最低:8,512K

最高:13,516K

内存不是固定在某个值,而是在这个区间内不停的变动,而且幅度比较大。估计是内存增长到一定的范围(上一章说的那个阀值?)就自动进行回收了,所 以,内存马上又下降,从而使得我们看到内存在不停的波动。

现在我们每次得到10000个对象之后,就手动处理一下内存回收,看什么结果,代码改为这样:
[csharp] view plain copy

for (int i = 0; i < 1000000; ++i)    
{    
    for (int j = 0; j < 10000; ++j)    
    {    
        SocketAsyncEventArgs e = new SocketAsyncEventArgs();    
    }    
    GC.Collect(2);  
    System.Threading.Thread.Sleep(1000);    
}    

最低:8992

最高:10608

内存看起来(至少在我的机器上试了几次)稳定多了,变动的幅度也相比小一些,甚至有一会一直在(9800K-9900K)两个数字之间变化

上面的两个实验可能不是很准确,我的大概的意图是了解一下回收的效果,也可以让大家大概了解一下。

现在我们继续这个实验,当然要结合我上一文章的例子。代码如下:
[csharp] view plain copy

namespace Test2  
{  
    class Program  
    {  
        static void Main(string[] args)  
        {  
            Console.WriteLine("Press any key to start.");  
            Console.ReadKey();  

            for (int i = 0; i < 1000000; ++i)  
            {  
                for (int j = 0; j < 10000; ++j)  
                {  
                    SocketAsyncEventArgs e = new SocketAsyncEventArgs();  
                    e.SetBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1 }, 0, 10);  
                }  
                GC.Collect();  
                System.Threading.Thread.Sleep(1000);  
            }  
        }  
    }  
}  

这里把上一篇文章中的两个NEW动作都包含进来了,测试了一下,几乎是在几十秒内内存增长了60多M!

OK,问题就是出在这里了。

这里有一个明显的疑点:为什么加了这个SetBuffer 之后,内存的需求量就开始暴增呢?难道是这里new出来的byte数组没有被释放?从而导致两个新创建的对象都无法成为 dead object ?

修改代码如下:
[csharp] view plain copy

for (int i = 0; i < 1000000; ++i)  
{  
    for (int j = 0; j < 10000; ++j)  
    {  
        SocketAsyncEventArgs e = new SocketAsyncEventArgs();  
        e.SetBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1 }, 0, 10);  

        <em>e.SetBuffer(null, 0, 0)</em>;  
    }  
    GC.Collect();  
    System.Threading.Thread.Sleep(1000);  
}  

测试了一下,非常正确,内存稳定了~~~

现在根据这个发现去修改上一文章中的例子,每次发送完数据就取消SocketAsyncEventArgs 对 byte数组的引用。代码如下:
[csharp] view plain copy

public bool SendAsync(byte[] bytsCmd)  
{  
    //下面5行为新增加、修改的行  
    SocketAsyncEventArgs e = new SocketAsyncEventArgs();  
    e.Completed += (object s, SocketAsyncEventArgs _e) =>  
    {  
        e.SetBuffer(null, 0, 0);  
    };  

    try  
    {  
        e.SetBuffer(bytsCmd, 0, bytsCmd.Length);  
    }  
    catch (Exception ex)  
    {  
        lock (Program.s_lst)  
            Program.s_lst.Add(e);  
        System.Diagnostics.Debug.WriteLine("SetBuffer exception." + ex);  
        return false;  
    }  

    try  
    {  
        if (m_skt.SendAsync(e))  
        {//Returns true if the I/O operation is pending.  
            return true;  
        }  
    }  
    catch (Exception ex)  
    {  
        System.Diagnostics.Debug.WriteLine("SendAsync exception." + ex);                  

        return false;  
    }  

    e.SetBuffer(null, 0, 0);//新增加的行  

    return true;  
}  

上面的代码还是按最初的方式处理:发送一次数据生成一个SocketAsyncEventArgs对象,但是,为了能使GC能正确的回收,在命令完成后,我们增加了e.SetBuffer(null,0,0) 这一句,以表明这几个对象都不用了,已经是 dead objects 了!

修改完后的代码测试 了20分钟,内存一直稳定在30M左右!

结论:

GC不是我们想象的那样聪明!两个dead object如果互相引用,它就没办法判断是否应该回收了!所以,用.net编程时,也要小心内存的处理,时刻要为GC着想、帮它一把!:)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值