Is it necessary to call gc.collect explicitly sometimes with COM objects

Here is the function:

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 message classes used above. If you look in the function above, you will see:

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

Here is what I had to do to fix the problem we have. Today I played with memory profilers (ants and dottrace) to see if I could understand anything, but so far everything looks good. I am not low, who does not know what is happening with the wind.

The error I get: Error in IMAPISession :: OpenEntry: MAPI_E_TOO_BIG

The line where I always get the error is commented out in the code below. I always get an error after ~ 450 iterations.

Is this one of several uses of gc.collect when you work with COM objects?

Here is the CreateNDR function with the line in which the error occurs:

  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 
+4
source share
2 answers

There are cases when a program simply does not consume enough collected memory to cause a finalizer stream sufficient to clear resources. For example, the Thread class is a problem. It consumes 5 operating system descriptors, but does not have a Dispose () method for their early release. And COM classes like the ones you use, the managed shell created by the CLR for them has a finalizer, but it does not implement IDisposable. Examples of classes in which a user program simply cannot efficiently or reliably call Dispose ().

GC.Collect () was made for such cases. Also call GC.WaitForPendingFinalizers () since you really want to.

Your use is correct and justified. You need to configure it.

+5
source

What you are probably using includes a managed heap that doesn't understand the true memory size of live COM objects.

In terms of .NET runtime, your COM object has a trace of an automatically generated Runtime Callable Wrapper (RCW) that is tiny. You may have created an instance of a COM object that is stored on a huge amount of memory, but this memory does not live in the .NET memory heap, so the garbage collector does not feel any pressure to clear it.

Forcing the garbage collector to work in this situation seems right. He will dispose of any unrestored RCWs, which, in turn, will reset the counter of COM links to COM objects, which will lead to its release (provided that there are no other COM links to this object, of course).

+4
source

Source: https://habr.com/ru/post/1391924/


All Articles