Вы должны вызывать gc.collect иногда иногда с COM-объектами - PullRequest
4 голосов
/ 20 января 2012

Вот функция:

        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

Используемые выше классы сообщений: Искупление . Если вы посмотрите на функцию выше, вы увидите:

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

Что я и должен был сделать, чтобы решить нашу проблему. Этим утром я играл с профилировщиками памяти (муравьи и точка), чтобы посмотреть, смогу ли я что-нибудь выяснить, но пока все выглядит хорошо. Я не на низком уровне, не знаю, кто знает все тонкости WindGB.

Я получаю ошибку: Ошибка в IMAPISession :: OpenEntry: MAPI_E_TOO_BIG

Строка, где я всегда получаю ошибку, прокомментирована в коде ниже. Я всегда получаю ошибку после ~ 450 итераций.

Это один из немногих случаев, когда вам нужно использовать gc.collect, когда вы имеете дело с COM-объектами?

Вот функция 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

Ответы [ 2 ]

5 голосов
/ 20 января 2012

В некоторых случаях программа просто не использует достаточно памяти для сбора мусора, чтобы вызвать поток финализатора, достаточный для очистки ресурсов. Проблемой является, например, класс Thread. Он использует 5 дескрипторов операционной системы, но не имеет метода Dispose (), чтобы выпустить их раньше. И COM-классы, подобные тем, которые вы используете, управляемая оболочка, которую CLR создает для них, имеет финализатор, но не реализует IDisposable. Примеры классов, в которых пользовательская программа просто не может эффективно или надежно вызвать Dispose ().

GC.Collect () был создан для таких случаев. Также вызовите GC.WaitForPendingFinalizers (), поскольку именно этого вы действительно хотите.

Ваше использование является правильным и оправданным. Вы должны настроить его.

4 голосов
/ 20 января 2012

То, с чем вы, вероятно, сталкиваетесь, включает в себя управляемую кучу, не понимающую истинного объема памяти живых COM-объектов.

С точки зрения среды выполнения .NET у вашего COM-объекта есть след автоматически созданного Runtime Callable Wrapper (RCW), который крошечный. Возможно, вы создали экземпляр COM-объекта, который хранит огромный объем памяти, но эта память не находится в куче памяти .NET, поэтому сборщик мусора не чувствует никакого давления для его очистки.

Принудительное выполнение сборщика мусора в этой ситуации кажется правильным решением. Он избавится от любых не связанных ссылок RCW, которые, в свою очередь, сбросят счетчик ссылок COM на объекты COM, что приведет к его освобождению (конечно, при условии, что других ссылок на этот объект нет).

...