Question Streams and the GC

Jauch

New member
Joined
Apr 27, 2011
Messages
4
Programming Experience
5-10
Hi, I think I have a problem with the GC and Streams.

In my app, I have an object LOG, that writes to a file some events, so the user can always know what happened (the app run every 30 minutes with windows task schedule).

The problem is that sometimes an exception (many, in fact) can be "triggered", bypassing the normal flow of the app.

These exceptions are caught, the messages are saved to the LOG file and then the app throw them again, so that the app can continue to work.

But sometimes, an exception can not be handled and the program is "aborted".
In this case, I put, at the "FINALIZE" method of the LOG object to flush all information to the file.

The problem is that when the "FINALIZE" method is called, after the ending of the app, the stream is alread closed, thus, I lost the last lines stored in the LOG, that weren't saved to the file...

Is this a CG issue? Like it is closing the streams (that are protected variables inside the object) BEFORE finalizing the object?

The object is like this:

VB.NET:
   Public Class Log : Inherits Mohid.Base.Message
        Protected _Lines As List(Of String) = Nothing
        Protected _File As String
        Protected _FileMode As FileMode
        Protected _SaveOnAdd As Boolean
        Protected _StreamW As StreamWriter = Nothing
        Protected _FileStream As FileStream = Nothing
        Protected _AutomaticSaveOnKill As Boolean

        Public Sub New()
            _Lines = New List(Of String)
            _File = ""
            _FileMode = FileMode.Create
            _SaveOnAdd = False
            _AutomaticSaveOnKill = False
        End Sub

        Protected Overrides Sub Finalize()
            CloseFile()
        End Sub

        Public Function SaveToFile() As Boolean
            Return SaveToFile(_StreamW)
        End Function

        Public Function SaveToFile(ByRef sw As StreamWriter) As Boolean
            If sw Is Nothing Then Return False

            If sw.BaseStream.CanWrite Then
                For Each line As String In _Lines
                    sw.WriteLine(line)
                Next

                sw.Flush()
            End If

            Return True
        End Function

        Public Sub OpenFile()
            CloseFile()

            _FileStream = New FileStream(_File, _FileMode, FileAccess.Write, FileShare.None)
            _StreamW = New StreamWriter(_FileStream)
        End Sub

        Public Sub CloseFile()
            If Not _StreamW Is Nothing Then
                If _SaveOnAdd Then
                    If _StreamW.BaseStream.CanWrite Then _StreamW.Flush()
                ElseIf _AutomaticSaveOnKill Then
                    SaveToFile()
                End If
            End If

            If Not _FileStream Is Nothing Then
                _FileStream.Close()
            End If
        End Sub

    End Class

Normally the CloseFile() method is called only once by the app, but when an exception is triggered, the method may not be called at all (depending the point where the exception was triggered) and I would like to the Finalize method close the file (doing the flush) without I need to "catch" the exception...

Thanks!
 
Last edited:
At the time finalizer is called the object is long gone from managed code. This means nothing prevents FileStream from being collected before StreamWriter. So what happens here is that the FileStream has been finalized already.
Object.Finalize said:
The finalizers of two objects are not guaranteed to run in any specific order, even if one object refers to the other. That is, if Object A has a reference to Object B and both have finalizers, Object B might have already finalized when the finalizer of Object A starts.
You can set AutoFlush to True for the StreamWriter to make it send all writes directly to file.

For better memory management you should not include a finalizer unless you have unmanaged resources to clean up. To provide cleanups callable from client code you should implement IDisposable interface.
 
I'll have this in mind from now on.

As a "workaround", I placed a "try" block at the topmost function of the object the uses the log and if it catchs an exception, call CloseFile and throws the exception again.

Thanks for the explanation JohnH. :)
 
Back
Top