I have a method that goes through a 670 MB text file line by line. The method searches for specific letter combinations at the beginning of a line (4 characters long) and inserts their line number into a Dictionary, depending on the type. Even after I have opened the program for 20 minutes, the RAM consumption does not decrease and remains constant at 488.5 MB. And this, although I have emptied and released all dictionaries (=Nil). Why does the memory consumption remain constantly so high, although I release the memory internally?
file = TextInputStream.Open(GetOpenFolderItem("txt"))
fileLength = file.Length
[code]Private Sub RunAnalyzeThread(sender As Thread)
myDict1 = New Dictionary
myDict2 = New Dictionary
myDict3 = New Dictionary
myDict4 = New Dictionary
myDict5 = New Dictionary
myDict6 = New Dictionary
myDict7 = New Dictionary
Const chunkSize = 5000
Dim mCurrentLineNumber As Integer
Dim residual, mCurrentLine As String
Dim tArray() As String
Dim result As String
While Not file.EOF
Dim bytesToRead As Integer = Min(fileLength - file.PositionB, chunkSize)
result = file.Read(bytesToRead)
tArray = result.Split(&uA)
' insert residual text of incomplete line from previous read
tArray(0) = residual + tArray(0)
Dim nLines As Integer = tArray.Ubound - 1
' save incomplete line at end of current chunk for next read
residual = tArray(nLines + 1)
For i As Integer = 0 To nLines
' Process complete lines of text here
mCurrentLine = tArray(i)
Select Case mCurrentLine.Left(4)
myDict1.Value(mCurrentLineNumber) = Nil
myDict2.Value(mCurrentLineNumber) = Nil
myDict3.Value(mCurrentLineNumber) = Nil
myDict4.Value(mCurrentLineNumber) = Nil
myDict5.Value(mCurrentLineNumber) = Nil
myDict6.Value(mCurrentLineNumber) = Nil
myDict7.Value(mCurrentLineNumber) = Nil
mCurrentLineNumber = mCurrentLineNumber + 1
' If file doesn't end with an EndOfLine, then the residual string
' will contain the final partial line which still needs to be processed.
If residual <> "" Then
' Process final partial line of text
result = “”
residual = “”
mCurrentLine = “”
file = Nil
myDict1 = Nil
myDict2 = Nil
myDict3 = Nil
myDict4 = Nil
myDict5 = Nil
myDict6 = Nil
myDict7 = Nil
Do you have a test project for this?
and are you sure this compiles?
[quote=385818:@Christian Schmitz]Do you have a test project for this?
and are you sure this compiles?[/quote]
Of course, I just made a test project. You can see it here: Test-Projekt
The working memory remains constant at approx. 109 MB after analysis.
I think the thread and class may never be released as they depend on each other.
Could that be?
AddHandler causes a new reference.
[quote=385826:@Christian Schmitz]I think the thread and class may never be released as they depend on each other.
Could that be?[/quote]
What do you mean?
If I add this at the end of “RunAnalyzeThread”
Xojo.Core.Timer.CallLater(0, AddressOf Clean)
and add the new method “Clean”
RemoveHandler AnalyzeThread.Run, AddressOf RunAnalyzeThread
AnalyzeThread = Nil
nothing happens. The memory consumption remains on top.
You have a circular reference.
e.g. you could use WeakAddressOf here to make it a weak connection to the run handler.
Please remove the AnalyzeThread.Kill call here and setting to nil doesn’t help here, too.
When RunAnalyzeThread finishes, you could set the AnalyzeThread property to nil, so the reference is gone there.
Thank you for the note with WeakAddressOf. That’s what I would have tried next.
However, the RAM still remains high. Something seems to be not working with the memory management of Xojo…!?
It’s maybe just 1 MB which may be leaked. Don’t worry.
The memory management is fine. The last years it was always the developer at fault.
[quote=385832:@Christian Schmitz]It’s maybe just 1 MB which may be leaked. Don’t worry.
The memory management is fine. The last years it was always the developer at fault.[/quote]
OK, and that means that there is a bug in my code somewhere, or that I just have to accept the memory allocation because it is normal (and the memory is not freed)?
Thread references class
Class references thread
As both references are staying, the memory can’t be released.
But with weak reference and setting back thread reference to nil on the end of the run method, it should be away.
If I’m right at the end of the method from above add
sender = Nil or
AnalyzeThread = Nil does not change. I didn’t either, if I sender/AnalyzeThread = Nil as described above into a Clean method by inserting
Xojo.Core.Timer.CallLater(0, AddressOf Clean) at the end of RunAnalyzeThread.
Or could it be that the dictionaries are not released properly?
Thank you for your time, patience and input.
You can analyse your app with Instruments. Select leaks. I did a quick run and the memory leaks are very small. A couple of hundred bytes.
The question is now: is the value from the Activity Viewer correct? This doesn’t go down. Isn’t there a function System.MemoryUsed or similar that you can check?
[quote=385854:@Beatrix Willius]You can analyse your app with Instruments. Select leaks. I did a quick run and the memory leaks are very small. A couple of hundred bytes.
The question is now: is the value from the Activity Viewer correct? This doesn’t go down. Isn’t there a function System.MemoryUsed or similar that you can check?[/quote]
with Instruments, we looked at the matter yesterday and observed exactly the same thing as you.
The note with the function
Runtime.MemoryUsed is very interesting. If I query the value before the deletion calls of the dictionaries, it moves around 91 MB (approximately the display of the task manager). If I query the value again after deleting the dictionaries, it shows approx. 21 MB. This means that the program seems to really free up memory, but the display in the task manager of macOS is not updated. On the other hand, it is very strange that the task manager still displays the wrong value after 20 minutes. Very interesting, at least if the value returned is correct!
Excuse me if my question does not make sense: is the job done in a different window, and at the end of the job that window gets closed? Because threre is that old bug that closing a window (Mac) does not release its memory, unless in the cancelclose event of that window one does not put the proper declare (case #43484). But I’m not a pro, and my reasoning might be utterly out of mark.
@Carlo Rubini: this would be a couple of MBs only. But not a bad idea.
It’s not the thread. After eliminating the thread the memory consumption stays the same. When I click on the button a couple of times the memory used goes up only a couple of MBs.
However, when I change from 64bit to 32bit then I only get 29MB in the Activity Monitor.
I didnt tried this, but thats also interesting. So could be a 64-bit Bug in Xojo?
@Martin T: could be. At least it looks very odd.
I’ve done a bit goggling about the differences between Activity Monitor and what Instruments shows. Have a look at
When I execute the code a couple of times the memory in Activity Monitor goes up. This shouldn’t be the case.
Well, first you need to know that the memory management functions in the C library will allocate memory in chunks, e.g. 64 KB.
The app may ask for small peaces like 16 byte. But the 64K block may not be freed until all smaller blocks inside it are released. So fragmentation may cause Activity Monitor to show more allocated memory than really is used.
The only way to find a leak is to make a sample app:
- A method with the calls which may leak
- A button which calls that method a dozen times.
Now run the app and click the button a few times. After each click, you may see a memory increase or not.
First call may fill some caches, so please don’t complain if first run uses memory.