当前位置:编程学习 > C#/ASP.NET >>

垃圾回收导致程序崩溃(GC.Collect crash)

垃圾回收导致程序崩溃(GC.Collect crash)

环境:
winxp(sp3),vs2005,.net 2.0,c#语言,oracle 10g

程序概述:
程序调用第三方提供的dll操作串口,控制串口上连接的设备(2个串口,分别使用2个厂商提供dll文件),在与串口通信的过程中,程序还通过TCP协议连接另一台主机,获取一些数据发给串口,同时,程序还通过oralce 10g客户端操作数据库服务器。程序在运行过程中,会向串口发送多次命令,每次发送一批(大概几十条,每条256字节以内)。每个命令就是一串字节数组写向串口。

问题描述:
向串口发送一批命令,程序在CLR执行垃圾回收时崩溃,直接弹系统错误对话框(就是发送错误报告的那个对话框),为了查找问题,我在每次向串口发送命令之前,都强制执行GC.Collect()来回收垃圾,发现每次都是到那条指令前执行GC.Collect()程序才崩溃。我用DebugDiag捕捉到dump文件,用WinDbg工具分析这个dump文件,具体分析结果如下,请哪位达人帮忙分析解答。谢谢!jizhiguo@netease.com

-------------WinDbg分析结果----------

Loading Dump File [D:\Program Files\DebugDiag\Logs\Misc\NSIISys.exe__PID__2456__Date__10_21_2009__Time_02_50_39PM__31__Manual Dump.dmp]
User Mini Dump File with Full Memory: Only application data is available

Comment: 'Dump created by Debug Diagnostic Tool'
Symbol search path is: SRV*d:\websymbols*http://msdl.microsoft.com/download/symbols;D:\jizg\NSIISys\bin\Debug;D:\Symbols
Executable search path is:
Windows XP Version 2600 (Service Pack 3) MP (2 procs) Free x86 compatible
Product: WinNt, suite: SingleUserTS Personal
Machine Name:
Debug session time: Wed Oct 21 14:50:39.000 2009 (GMT+8)
System Uptime: 0 days 3:33:01.279
Process Uptime: 0 days 0:01:24.000
................................................................
......................
Loading unloaded module list
.....
eax=015ca5ac ebx=013c3518 ecx=79308060 edx=0015b210 esi=01412b28 edi=015ca908
eip=7c92e514 esp=0013ecc0 ebp=0013ed54 iopl=0        nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000            efl=00000246
ntdll!KiFastSystemCallRet:
7c92e514 c3              ret
0:000> kbn
# ChildEBP RetAddr  Args to Child             
00 0013ecbc 77d19418 7b1d8ea8 09c75026 79e7a6e8 ntdll!KiFastSystemCallRet
*** WARNING: Unable to verify checksum for System.Windows.Forms.ni.dll
01 0013ed54 7b1d8997 00000000 00000004 00000000 user32!NtUserWaitMessage+0xc
02 0013edac 7b1d87e1 01466204 00000000 00000000 System_Windows_Forms_ni+0x208997
03 0013eddc 7b6ede2b 015b1034 01457990 000301c6 System_Windows_Forms_ni+0x2087e1
04 0013edf4 7b7225ab 09c75026 79e7a6e8 0013f0a8 System_Windows_Forms_ni+0x71de2b
05 0013ee80 7b7227c3 00e7bd99 0013f1f4 79edeff8 System_Windows_Forms_ni+0x7525ab
06 0013eed0 7b1e2d98 01442108 013dff40 013dfee0 System_Windows_Forms_ni+0x7527c3
07 0013eee8 7b88a746 014cd8d8 013dfee0 014cd8d8 System_Windows_Forms_ni+0x212d98
08 0013eefc 7b7467de 013dfee0 00000016 013dfee0 System_Windows_Forms_ni+0x8ba746
09 0013ef28 7b746e40 013dff40 014420ec 013dfee0 System_Windows_Forms_ni+0x7767de
0a 0013ef6c 7b746339 014cd8a4 013dfee0 0013ef94 System_Windows_Forms_ni+0x776e40
0b 0013ef7c 7ba34fcb 00000002 013dfee0 014cd8a4 System_Windows_Forms_ni+0x776339
0c 0013ef94 7b740cb6 00000002 0000000d 0000002a System_Windows_Forms_ni+0xa64fcb
0d 0013efc4 7b74a89d 09c75026 00000000 00000000 System_Windows_Forms_ni+0x770cb6
0e 0013f010 7b6f76f3 013f7af8 0000000f 000f002a System_Windows_Forms_ni+0x77a89d
0f 0013f094 7ba2a136 00000001 00100000 09c75026 System_Windows_Forms_ni+0x7276f3
10 0013f0f4 7b1d1dca 0015b210 0013f170 7b1f2051 System_Windows_Forms_ni+0xa5a136
11 0013f100 7b1f2051 09c75026 79e7a6e8 0013f338 System_Windows_Forms_ni+0x201dca
12 0013f170 7b1affc6 0013f1a8 013f7d24 0013f188 System_Windows_Forms_ni+0x222051
13 0013f180 7b1c86a0 0013f19c 7b1c8621 00000000 System_Windows_Forms_ni+0x1dffc6
0:000> .loadby sos mscorwks
0:000> !clrstack
OS Thread Id: 0xbb8 (0)
ESP      EIP   
0013eccc 7c92e514 [InlinedCallFrame: 0013eccc] System.Windows.Forms.UnsafeNativeMethods.WaitMessage()
0013ecc8 7b1d8ea8 System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32, Int32, Int32)
0013ed64 7b1d8997 System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext)
0013edb8 7b1d87e1 System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)
0013ede8 7b6ede2b System.Windows.Forms.Application.RunDialog(System.Windows.Forms.Form)
0013edfc 7b7225ab System.Windows.Forms.Form.ShowDialog(System.Windows.Forms.IWin32Window)
0013ee88 7b7227c3 System.Windows.Forms.Form.ShowDialog()
0013ee8c 00e7bd99 NSIISys.FrmMain.自动发卡机ToolStripMenuItem1_Click(System.Object, System.EventArgs)
0013eedc 7b1e2d98 System.Windows.Forms.ToolStripItem.RaiseEvent(System.Object, System.EventArgs)
0013eef4 7b88a746 System.Windows.Forms.ToolStripMenuItem.OnClick(System.EventArgs)
0013ef04 7b7467de System.Windows.Forms.ToolStripItem.HandleClick(System.EventArgs)
0013ef30 7b746e40 System.Windows.Forms.ToolStripItem.HandleMouseUp(System.Windows.Forms.MouseEventArgs)
0013ef74 7b746339 System.Windows.Forms.ToolStripItem.FireEventInteractive(System.EventArgs, System.Windows.Forms.ToolStripItemEventType)
0013ef88 7ba34fcb System.Windows.Forms.ToolStripItem.FireEvent(System.EventArgs, System.Windows.Forms.ToolStripItemEventType)
0013efa0 7b740cb6 System.Windows.Forms.ToolStrip.OnMouseUp(System.Windows.Forms.MouseEventArgs)
0013efcc 7b74a89d System.Windows.Forms.ToolStripDropDown.OnMouseUp(System.Windows.Forms.MouseEventArgs)
0013f018 7b6f76f3 System.Windows.Forms.Control.WmMouseUp(System.Windows.Forms.Message ByRef, System.Windows.Forms.MouseButtons, Int32)
0013f0a4 7ba2a136 System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)
0013f0a8 7b1d1dca [InlinedCallFrame: 0013f0a8]
0013f108 7b1f2051 System.Windows.Forms.ToolStrip.WndProc(System.Windows.Forms.Message ByRef)
0013f10c 7b1affc6 [InlinedCallFrame: 0013f10c]
0013f188 7b1c86a0 System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef)
0013f190 7b1c8621 System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)
0013f1a4 7b1c84fa System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)
0013f338 003b20a4 [NDirectMethodFrameStandalone: 0013f338] System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG ByRef)
0013f348 7b1d8d2e System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32, Int32, Int32)
0013f3e4 7b1d8997 System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext)
0013f438 7b1d87e1 System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)
0013f468 7b195931 System.Windows.Forms.Application.Run(System.Windows.Forms.Form)
0013f47c 00e700ae NSIISys.Program.Main()
0013f69c 79e71b4c [GCFrame: 0013f69c]  --------------------编程问答-------------------- 为什么要强制调用GC.Collect()来回收垃圾?
让其自己工作不好吗?
如果调用了非托管的东西,手动释放掉就是了,不必要GC强制回收,频繁的GC强制回收会导致不预期的错误的.
而且不好查 --------------------编程问答-------------------- 个人分析,你应该在强制回收时配合SuppressFinalize()方法来挂起线程。另外,应该说,垃圾回收本身只会清除不再使用的资源,因此不会因为这个回收了还在使用的资源而出错,主要是你现在强制垃圾回收时候,GC就会将所有线程挂起,那么这个挂起很有可能就是你总出错的原因,尤其是在于外部设备通信过程中,会导致实时的数据交换被中断,从而在你代码中出现错误。 --------------------编程问答-------------------- hi,lerit,thx for your response.

我加入GC.Collect()的目的就是确认是GC引起的错误。我的代码在没有手动调用GC.Collect()的情况下,出现崩溃的时间不定(1分钟~10分钟不等),但一定会发生。后来加了GC.Collect()就能确定地重现问题了。具体重现的步骤如主贴所述。发生崩溃时,程序正在和串口通信。

--------------------编程问答-------------------- 开始的时候,我没有在每条串口指令前调用GC.Collect(),而是由CLR负责垃圾回收,这样,程序崩溃就是不可预期的,有可能是几十条串口指令就崩溃,也可能几百条才崩溃。

后台为了定位问题,我才手动在每条串口指令前调用GC.Collect(),这样一来,崩溃就可准确重现了,一定是在执行完n条串口指令后,在执行n+1 条串口指令前调用GC.Collect()时崩溃。注:n是常数,且这n条指令每次执行都是一样的指令。第n+1条也是每次执行都是一样的指令。

高手帮我分析分析,已经好几天了,找不到解决办法。 --------------------编程问答-------------------- “后台为了定位问题”应改为“后来为了定位问题” --------------------编程问答-------------------- 应该不是gc崩溃,而是你的通讯组件出问题,你尝试下把你通讯组件内用到的对象都用GC.KeepAlive保持住 --------------------编程问答-------------------- 没有什么通信组件,只是利用C#的SerialPort类而已。代码就这点,如下:



using System;
using System.Collections.Generic;
using System.Text;
using SysUtilities;

namespace IFDMgr
{
    public class SerialPortMgr : IIFDMgr
    {
        private System.IO.Ports.SerialPort serialPort = null;
        private int sequence = 0;//执行指令的序列号,0~7循环取值

        public SerialPortMgr(string port)
        {
            this.serialPort = new System.IO.Ports.SerialPort(port);
            this.serialPort.BaudRate = 115200;
            this.serialPort.WriteTimeout = 10 * 1000;
            this.serialPort.ReadTimeout = 10 * 1000;
            sequence = 0;
        }

        ~SerialPortMgr()
        {
            if (serialPort.IsOpen)
            {
                ShutDownDevice();
            }
        }

        private bool StartUpDevice(string portName)
        {
            try
            {
                if (!serialPort.IsOpen)
                {
                    serialPort.Open();
                    string cmd = "FF007003AAAAAA26";//与机具握手
                    byte[] resp=null;
                    ExecRawCmd(cmd, ref resp, 0);
                }
            }
            catch (Exception exc)
            {
                throw new Exception(string.Format("打开端口{0}失败!", portName));
            } 
            return true;
        }

        private bool ShutDownDevice()
        {
            if (serialPort.IsOpen)
            {
                try
                {
                    serialPort.Close();
                }
                catch (Exception)
                {
                    throw new Exception(string.Format("关闭端口{0}失败!", serialPort.PortName));
                }
            }
            return true;
        }

        private uint ExecCmd(string command, ref byte[] resp, int cardJack)
        {

            System.GC.Collect();

            uint sw = uint.MinValue;
            string comm = command.Replace(" ", "");
            if (comm.Length < 4)
            {
                throw new Exception(string.Format("指令长度小于4,错误!指令:{0}", command));
            }
            if (!serialPort.IsOpen)
            {
                throw new Exception(string.Format("串口{0}未打开!", serialPort.PortName));
            }
            byte[] response = new byte[256];
            byte[] sendBuffer =
                SysHelper.CombineBytes(
                new byte[] { 0xFF },
                SysHelper.Int2Bytes(++sequence % 8, 1),
                new byte[] { 0x60 },//pro卡命令
                SysHelper.Int2Bytes(comm.Length / 2, 1),
                SysHelper.HexString2Bytes(comm)
                );
            sendBuffer = SysHelper.Combine2Bytes(
                sendBuffer,
                new byte[] { SysHelper.GetXOR(sendBuffer) }
                );

            Log.Log.PutLog(string.Format("串口指令: {0}", SysHelper.Bytes2HexString(sendBuffer)));
           
            int retryCount = 1;//由于无线接口,失败指令重试1次
            int receiveCount = 0;
            do
            {
                serialPort.DiscardInBuffer();
                serialPort.DiscardOutBuffer();
                serialPort.Write(sendBuffer, 0, sendBuffer.Length);
                System.Threading.Thread.Sleep(200);
                receiveCount = serialPort.Read(response, 0, serialPort.BytesToRead);
                if (receiveCount < 5 ||//长度错
                    response[0] != 0xFF ||//帧头错
                    response[1] != (byte)(sequence % 8) ||//序列号错
                    response[2] != 0x00 ||//指令执行失败
                    response[3] != receiveCount - 5 ||//数据长度错
                    // 异或校验错
                    response[receiveCount - 1] != SysHelper.GetXOR(SysHelper.SubBytes(response, 0, receiveCount - 1)))
                {
                    if (retryCount-- > 0)
                    {
                        continue;
                    }
                }

            } while (retryCount-- > 0 && response[2] != 0x00);



            Log.Log.PutLog(string.Format("串口返回: {0}", SysHelper.Bytes2HexString(
                SysHelper.SubBytes(response,0,receiveCount))));


            if (receiveCount < 5)//长度错
            {
                throw new Exception(string.Format("执行指令:{0},返回数据的长度错误!返回数据:{1}", command, SysHelper.Bytes2HexString(SysHelper.SubBytes(response, 0, receiveCount))));
            }
            if (response[0] != 0xFF)//帧头错
            {
                throw new Exception(string.Format("执行指令:{0},返回数据的帧头应该为FF!返回数据:{1}", command, SysHelper.Bytes2HexString(SysHelper.SubBytes(response, 0, receiveCount))));
            }
            if (response[1] != (byte)(sequence % 8))//序列号错
            {
                throw new Exception(string.Format("执行指令:{0},返回数据的序号!返回数据:{1}", command, SysHelper.Bytes2HexString(SysHelper.SubBytes(response, 0, receiveCount))));
            }
            if (response[2] != 0x00)//指令执行失败
            {
                throw new Exception(string.Format("执行指令:{0},返回数据的成功标志!=00,失败!返回数据:{1}", command, SysHelper.Bytes2HexString(SysHelper.SubBytes(response, 0, receiveCount))));
            }
            if (response[3] != receiveCount - 5)//数据长度错
            {
                throw new Exception(string.Format("执行指令:{0},返回数据的长度域!返回数据:{1}", command, SysHelper.Bytes2HexString(SysHelper.SubBytes(response, 0, receiveCount))));
            }
            // 异或校验错
            if (response[receiveCount - 1] != SysHelper.GetXOR(SysHelper.SubBytes(response, 0, receiveCount - 1)))
            {
                throw new Exception(string.Format("执行指令:{0},返回数据的异或校验失败!返回数据:{1}", command, SysHelper.Bytes2HexString(SysHelper.SubBytes(response, 0, receiveCount))));
            }

            resp = SysHelper.SubBytes(response, 4, response[3] - 2);
            sw = SysHelper.SubBytes(response, receiveCount - 3, 2)[0];
            sw <<= 8;
            sw += SysHelper.SubBytes(response, receiveCount - 3, 2)[1];
         
            if (sw != 0x9000)
            {
                throw new Exception(string.Format("执行指令失败!\r\n指令:{0}\r\n错误码:{1:X}\r\n卡座:{2}\r\n接口设备类型:{3}", command, sw, cardJack, this.GetType().ToString()));
            }
            return sw;
        }

        #region IIFDMgr 接口的实现

        bool IIFDMgr.isConnect()
        {
            return serialPort.IsOpen;
        }

        bool IIFDMgr.StartUpDevice(string port)
        {
            return StartUpDevice(port);
        }

        bool IIFDMgr.StartUpDevice()
        {
            return StartUpDevice(this.serialPort.PortName);
        }

        bool IIFDMgr.ShutDownDevice()
        {
            return ShutDownDevice();
        }

        uint IIFDMgr.ExecCmd(string command, ref byte[] resp, int cardJack)
        {
            return ExecCmd(command, ref resp, cardJack);
        }

        uint IIFDMgr.ExecCmd(string command, ref string resp, int cardJack)
        {
            uint ret = uint.MinValue;
            byte[] bResp = null;
            ret = ExecCmd(command, ref bResp, cardJack);
            resp = SysUtilities.SysHelper.Bytes2HexString(bResp);
            return ret;
        }

        #endregion
    }
}
--------------------编程问答-------------------- 无语了。。。我也遇到了这个问题。经过细心查证,发现是因为GC的Collect方法不支持XP SP3...
不信的可以去MSDN上查一下GC,然后看看支持的平台列表。不管哪个版本都没有对XP SP3提供支持。 --------------------编程问答--------------------
引用 8 楼 directray 的回复:
无语了。。。我也遇到了这个问题。经过细心查证,发现是因为GC的Collect方法不支持XP SP3...
不信的可以去MSDN上查一下GC,然后看看支持的平台列表。不管哪个版本都没有对XP SP3提供支持。


Platforms 的列表里面还没有 windows 98 SE 呢, 结果还不是好好的? 也没有 windows 2008,更没有 windows 2008 R2....是不是就不能用了呢?

Windows XP SP3 都已经被事实证明了无数次是没有问题的 --------------------编程问答-------------------- ls不懂得就不要装懂。没听说Windows XP SP2不能用GC.Collect。

至于lz,看dump的水平太差。搞不定的就联系微软技术支持帮你看看dump吧,http://support.microsoft.com。 

GC.Collect出异常一般都是你代码里面释放非托管资源的代码太烂,导致内存访问错误一类。当然,也可能是其他编码问题,和.NET自身都没什么关系。
补充:.NET技术 ,  C#
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,