【问题标题】:When I use Socket.IO, why I got an error An unhandled exception of type 'System.OutOfMemoryException'当我使用 Socket.IO 时,为什么会出现错误“System.OutOfMemoryException”类型的未处理异常
【发布时间】:2014-11-10 13:49:32
【问题描述】:

我编写了一个程序来获取屏幕截图并发送到服务器。每次,我都会截屏并转换为base64,然后使用Socket.IO发送。 (使用 SocketIOClient.dll)

 Dictionary<string, string> image = new Dictionary<string, string>();
 image.add("image", "");

 private void windowMonitorTimer_Tick(object sender, EventArgs e)
 {

     image["image"] = windowMonitorManager.MonitorScreen();
     client.getSocket().Emit("Shot", image);
 }

windowMonitorManager.MonitorScreen() 用于返回 base64 字符串。如果我不使用client.getSocket().Emit("Shot", image),程序可以正确运行,但如果我添加这一行,程序会停止 2 秒(发送近 80 次)并给我错误:

An unhandled exception of type 'System.OutOfMemoryException' occurred in mscorlib.dll

如果我不发送这么长的字符串,只是一个短字符串“hello”,它发送1600次就会出现同样的问题。

有人知道如何调试这个问题吗?

/////////////////////////////////////// ///////////////////////////////////////// ///////////////////////////////////////// //////////////////////

我尝试测试socket.Emit(),发现它有它的极限。

比如我发送一个10000000的字符串,88次后出现内存不足的问题。 如果我发送一个5000000的字符串,170次后,会出现同样的问题。

【问题讨论】:

  • 你有System.OutOfMemoryException的完整堆栈跟踪吗?
  • @PaulWilliams,如何使用 c# 跟踪完整堆栈?
  • @cindywmiao 你用的是什么IDE?视觉工作室?它应该有一个调试选项,您可以在其中插入断点并获取详细错误
  • 您可以尝试在您的client.GetSocket().Emit() 呼叫周围使用catch,捕获所有Exceptions,然后打印或保存或以某种方式查看exception.ToString()。但这不一定会奏效,因为程序可能内存不足。如果您调试程序,您应该能够在异常发生时看到异常并查看完整的堆栈跟踪。
  • 我不知道 SocketIOClient.dll 但可能你使用后必须关闭套接字?

标签: c# sockets socket.io


【解决方案1】:

内存不足主要是当进程消耗的内存比默认系统允许的内存大得多时引发的异常(如 32 位系统上的 2 GB),在 64 位系统上,它更高,但仍受限制在一定的实际限制下,它不是 2^64 的理论值,它因操作系统而异,也取决于底层 RAM,但对于单个进程来说足够大,现在这种情况可能由于多种原因而发生:

  • 内存泄漏(最突出),主要与非托管代码调用相关,如果有一个句柄或内存分配未解除分配或释放,在一段时间内会导致大量内存分配进程,因此当系统无法再映射时出现异常。

  • 1234563这在我的代码中:)
  • 这不是空引用或损坏,因此在这种情况下,直接堆栈跟踪将没有多大用处,只是因为您可能每次都得到不同的堆栈,它就像进程的堆栈一样线程,当异常发生时,它会产生误导,所以不要尝试这种方式。线程的执行方式不代表会导致内存泄漏,不同线程会有所不同。

如何调试:

可以采取一些简单的步骤来缩小范围,但在此之前,请确保您的调试版本包含所有已加载的进程二进制文件的有效 pdb 文件。

  • 要知道它是否泄漏,请通过任务管理器或最好通过 perfmon 监视进程“工作集”、“虚拟字节”计数器,因为它更准确,还提供可视化图表。

  • 现在泄漏就是泄漏,因此将 32 位系统中的地址空间增加到 3 GB 代替默认的 2 GB 之类的步骤只能在一段时间内有所帮助,但 perfmon 会告诉您是否存在稳定点,就像在几个在这种情况下,进程需要 2.2 GB 的内存,因此默认 2 GB 是不够的,但 boot.config 中的 3 GB 和用于微调的 UserVA 设置 3 GB 将有助于避免异常。

    • 如果您使用的是 64 位系统,则不必担心,但请确保您的二进制文件是为 X64 或任何 CPU 编译的,32 位二进制文​​件将作为 WOW 进程运行,并且对 64 位有限制系统也是。

    • 还可以尝试 sysinternals 中的一个小型实用程序句柄,在一个进程中批量运行它多次将提供泄漏句柄的详细信息,根据分配的文件、互斥锁等句柄的数量

    • 一旦您确认了真正的泄漏,而不是设置或配置或系统问题,那么内存分析器就会出现。在免费工具中,您可以通过 windbg、umdh 和泄漏诊断等免费工具获得大量信息,它们实际上会为您指出正在泄漏的确切堆栈跟踪。 umdh 和leakdiag 都是非常好的工具,它们让你知道泄露的功能。 Leakdiag 比 UMDH 更详尽,但对于运行时堆来说,UMDH 已经足够了

  • 专业的内存分析器,例如:

  • 点内存 - http://www.jetbrains.com/dotmemory/

  • 蚂蚁 - 红门 - http://www.red-gate.com/products/dotnet-development/ants-memory-profiler/

也非常好,我个人觉得点内存更有帮助,它有助于快速指向泄漏的函数或类型,只要你有正确的符号文件,不费吹灰之力。两者都有免费下载版本

大多数解决内存不足异常是一个渐进和迭代的过程,因为这个异常可能隐藏在内部深处,每次执行都会泄漏一块内存,并使整个过程陷入困境。如果您在使用特定工具时需要帮助,请告诉我,然后我们可以看看可以做些什么来进一步调试问题。快乐调试

【讨论】:

    【解决方案2】:

    我猜你是太频繁地运行你的计时器并且消耗内存到一个不可持续的地步。你试过降低它的频率吗?

    如果降低频率没有帮助,您的代码或 SocketIOClient.dll 库可能会泄漏内存。我建议您首先查看该库的使用情况,以确认您没有打开资源。

    【讨论】:

      【解决方案3】:

      在我看来,SocketIOClient DLL 中有一个错误。如果没有 DLL,我无法重现该问题,但追踪它听起来很容易。

      由于 C# 是一种垃圾回收语言,因此获得内存不足 (OOM) 的唯一方法是分配的内存过多而无法追踪到“根对象”。有几种根对象:

      • 静态变量(或线程静态)
      • 堆栈跟踪中的方法变量(局部变量/参数)

      您从这两个(直接/间接)引用的所有对象都会增加您的内存压力。如果你分配了不可用的内存,GC 会在抛出 OOM 之前先尝试释放内存;如果 GC 完成后没有足够的可用内存,则会抛出 OOM。

      发生这种情况的一个明显原因是您正在运行 32 位进程,这是当今 Visual Studio 中的默认设置。这可以在项目属性中修复。但是,大多数进程不需要超过 2 GB 的内存,因此您更有可能在某处“泄漏”内存。所以让我们分解一下:

      泄露的局部变量或参数

      解决此类OOM的方法:

      1. 打开 Visual Studio,ctrl d,e(或调试 -> 异常)
      2. 单击 OutOfMemoryException -> 选中“抛出”框
      3. 运行程序。

      当内存不足 (OOM) 发生时,您浏览堆栈跟踪(或“并行堆栈”)中的节点并检查变量的大小。在大多数情况下,它是导致问题的单个缓冲区或集合。前任在您的情况下,缓冲区可能会被套接字数据填满,而这些数据永远不会被清空。

      泄漏的静态变量

      OOM 的其他情况通常是缓冲区逐渐填满并具有对主树的引用。找到这些的最简单方法是使用内存分析器,例如 Red Gate / ANTS 内存分析器。在分析器中运行您的程序,拍摄一些快照并检查“大型实例”。

      一般来说,我通常尽量避免使用静态变量,这样可以解决很多问题。

      哦,在这种情况下...

      也许值得注意的是,那里有很多好的套接字库......即使我不知道 SocketIOClient 的具体细节,您可能需要考虑使用广泛支持、经过验证的套接字库,如 WCF/SOAP或 Protobuf。网上有很多关于如何在任何情况下使用这些的材料,所以如果问题出在 SocketIOClient 中,您可能需要考虑...

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-19
        • 2020-12-03
        • 1970-01-01
        • 2017-06-30
        • 2016-09-21
        • 1970-01-01
        相关资源
        最近更新 更多