.net - 有时是否必须使用 COM 对象显式调用 gc.collect

标签 .net com garbage-collection

这是函数:

        Private Sub LoadEmail()

        Dim loSession As RDOSession = Nothing
        Dim loMessages As RDOItems = Nothing

        Try
            moNDRs = New List(Of NonDeliveryRecord)
            loSession = New Redemption.RDOSession
            loSession.LogonExchangeMailbox(MailAccountName, MailServerName)
            loMessages = loSession.GetDefaultFolder(rdoDefaultFolders.olFolderInbox).Items
            Dim Counter = 0
            For Each loMessage As RDOMail In loMessages
                Counter += 1
                moNDRs.Add(CreateNDRRecord(loMessage))
                Marshal.ReleaseComObject(loMessage)
                loMessage = Nothing
                If Counter Mod 100 = 0 Then GC.Collect()
            Next


        Finally
            If loSession IsNot Nothing Then
                loSession.Logoff()
                Marshal.FinalReleaseComObject(loSession)
                loSession = Nothing
            End If

            If loMessages IsNot Nothing Then
                Marshal.FinalReleaseComObject(loMessages)
                loMessages = Nothing
            End If

        End Try

    End Sub

上面使用的消息类是 Redemption 。如果您查看上面的函数,您将看到:

If Counter Mod 100 = 0 Then GC.Collect()

这就是我必须做的来解决我们遇到的问题。今天早上我一直在使用内存分析器(ants 和 dottrace),看看我是否能弄清楚任何事情,但到目前为止一切看起来都很好。我水平不低谁知道windgb的来龙去脉。

我收到的错误是: IMAPISession::OpenEntry 中的错误:MAPI_E_TOO_BIG

下面的代码中注释了我总是收到错误的行。我总是在大约 450 次迭代后收到错误。

这是处理 COM 对象时必须使用 gc.collect 的少数几次之一吗?

这是 CreateNDR 函数,其中包含发生错误的行:

        Public Function CreateNDRRecord(ByVal voMessage As RDOMail) As NonDeliveryRecord

        Dim loItem As RDOReportItem = Nothing
        Dim loMatches As MatchCollection = Nothing
        Dim loNonDeliveryCode As NonDeliveryRecord = New NonDeliveryRecord
        Dim lsMessage As String = String.Empty


        Try
            loNonDeliveryCode.IsBadMessage = False
            loNonDeliveryCode.MailMessageId = voMessage.EntryID

            'Debug.Print(voMessage.MessageClass.Equals("REPORT.IPM.Note.NDR").ToString())
            If voMessage.MessageClass.Equals("REPORT.IPM.Note.NDR") Then 'error always happens here
                loItem = CType(voMessage, RDOReportItem)
                If voMessage.Recipients.Count <> 0 Then
                    loNonDeliveryCode.EmailAddress = voMessage.Recipients(1).Name
                End If
                loNonDeliveryCode.IsUndeliverable = True
                lsMessage = loItem.ReportText

            ElseIf voMessage.Subject.Contains(mconSeparator) Then
                loNonDeliveryCode.EmailAddress = voMessage.Subject.Substring(voMessage.Subject.LastIndexOf(mconSeparator) + mconSeparator.Length)
                loNonDeliveryCode.ErrorCode = String.Empty
                loNonDeliveryCode.IsUndeliverable = True
                lsMessage = voMessage.Body
            End If

            If loNonDeliveryCode.IsUndeliverable Then

                loMatches = GetErrorType(lsMessage)

                If loMatches.Count > 0 Then
                    loNonDeliveryCode.ErrorCode = loMatches(loMatches.Count - 1).Value
                End If

                Dim loNDRId = GetErrorCode(loNonDeliveryCode.ErrorCode)

                If loNDRId.Count > 0 Then
                    loNonDeliveryCode.ErrorCodeId = CType(CType(loNDRId(0), DataRow).Item("NonDeliveryCodeId"), Integer)
                    loNonDeliveryCode.ErrorDescription = CType(CType(loNDRId(0), DataRow).Item("Description"), String)
                    loNonDeliveryCode.MarkAsInvalid = CType(CType(loNDRId(0), DataRow).Item("MarkAsInvalid"), Boolean)
                Else
                    If voMessage.MessageClass.Equals("REPORT.IPM.Note.NDR") Then
                        loNonDeliveryCode.ErrorCode = String.Empty
                        loNDRId = GetErrorCode(loNonDeliveryCode.ErrorCode)
                        loNonDeliveryCode.ErrorCodeId = CType(CType(loNDRId(0), DataRow).Item("NonDeliveryCodeId"), Integer)
                        loNonDeliveryCode.ErrorDescription = CType(CType(loNDRId(0), DataRow).Item("Description"), String)
                        loNonDeliveryCode.MarkAsInvalid = CType(CType(loNDRId(0), DataRow).Item("MarkAsInvalid"), Boolean)
                    Else
                        loNonDeliveryCode.ErrorCode = String.Empty
                        loNonDeliveryCode.ErrorCodeId = 1
                    End If
                End If

            End If


            Return loNonDeliveryCode

        Catch Ex As Exception
            loNonDeliveryCode.IsUndeliverable = False
            loNonDeliveryCode.IsBadMessage = True
            Return loNonDeliveryCode

        Finally
            If loItem IsNot Nothing Then
                Marshal.FinalReleaseComObject(loItem)
                loItem = Nothing
            End If

            If voMessage IsNot Nothing Then Marshal.ReleaseComObject(voMessage)

            If loMatches IsNot Nothing Then
                loMatches = Nothing
            End If

        End Try

最佳答案

在某些情况下,程序没有消耗足够的垃圾收集内存来调用终结器线程来清理资源。例如,Thread 类就是一个麻烦制造者。它消耗 5 个操作系统句柄,但没有 Dispose() 方法来提前释放它们。和您正在使用的 COM 组件类一样,CLR 为它们创建的托管包装器具有终结器,但不实现 IDisposable。用户程序无法有效或可靠地调用 Dispose() 的类示例。

GC.Collect() 就是针对这种情况而设计的。还要调用 GC.WaitForPendingFinalizers(),因为这是您真正想要发生的事情。

您对它的使用是正确且站得住脚的。你必须调整它。

关于.net - 有时是否必须使用 COM 对象显式调用 gc.collect,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8933091/

相关文章:

c++ - 用户权限和 COM 对象

python - 我可以在 VBA 中使用我的 python 自定义对象吗?

java - 观察垃圾收集器 bin 一个对象

c# - 为什么非托管代码会将内存增加到特定限制?

c# - 如何在 IClientMessageInspector 中获取调用的操作名称?

c# - 如何将 C# 字符串变量从 Windows 窗体传递到 .BAT 文件

c++ - 为什么 std::unordered_set 不将 CComBSTR 类型作为键?

mongodb - MongoException : java. lang.OutOfMemoryError:超出了GC开销限制

c# - 无法从 ASP.NET 连接到 MySQL 数据库

c# - 如何将结构或类从 .NET COM dll 返回到使用的 VB6 应用程序?