Deallocate not freeing memory?

General FreeBASIC programming questions.
Post Reply
Pinkpuff
Posts: 26
Joined: Jun 10, 2005 18:45

Deallocate not freeing memory?

Post by Pinkpuff »

Perhaps I'm misunderstanding the function and/or usage of deallocate, but I was of the understanding that if you allocate a bunch of memory, it will increase the memory usage of your program, and if you deallocate it, it will reduce it back down. However I'm finding that in practice, only the first part of that statement is actually true.

Please let me know what I am doing wrong. Here is my code:

Code: Select all

type List

 contents as String ptr
 items as UInteger
 
 declare function ItemAt(index as UInteger) as String

 declare sub Append(item as String)
 declare sub Destroy()
 declare sub Remove(index as UInteger)

end type


function List.ItemAt(index as UInteger) as String

 dim result as String = ""
 
 if index <= items then result = contents[index - 1]
 
 return result

end function


sub List.Append(item as String)

 if items > 0 then
  items += 1
  contents = reallocate(contents, items * SizeOf(String))
  clear contents[items - 1], 0, SizeOf(String)
  contents[items - 1] = item
 else
  contents = callocate(SizeOf(String))
  contents[0] = item
  items = 1
 end if

end sub


sub List.Destroy()

 deallocate(contents)
 contents = 0
 items = 0
 
end sub


sub List.Remove(index as UInteger)

 if items > 0 then
  items -= 1
  for i as Integer = index - 1 to items - 1
   contents[i] = contents[i + 1]
  next
  contents = reallocate(contents, items * SizeOf(String))
 end if
 
end sub


dim a as List

print "press a key to create list"
getkey

for i as Integer = 1 to 100000
 a.Append(str(i))
next

print "press a key to destroy list"
getkey

a.Destroy()

print "press a key to create list"
getkey

for i as Integer = 1 to 100000
 a.Append(str(i))
next

print "press a key to destroy list"
getkey

a.Destroy()

print "done"
getkey
I ran a system monitor alongside the program. Before creating the first list, it was using something like 180-190 k. Upon creating the first list, it shot up to 5.9 Megs (as one might expect). However, upon supposedly destroying the list, it only went down to 4.7 Megs. Creating the list again caused it to once again shoot up, this time to 10.5 Megs, and then destroying it again it only went down to 9.3 Megs.

Any help would be appreciated. Thanks!
RockTheSchock
Posts: 252
Joined: Mar 12, 2006 16:25

Re: Deallocate not freeing memory?

Post by RockTheSchock »

The memory used by Strings is managed allready by freebasic. A string consists of a string descriptor which is basically a 12 byte udt with a pointer to the data an integer for the size and another integer field. fb hides that from you. If you want to manage your string memory use ZString PTR or byte arrays. What you are doing is to let fb make a String than you deallocate the string descriptor then freebasic has no possiblity to deallocate the actual string data, because there is no string descriptor anymore.

Lists are normally handled as linked ( double linked) lists, where you you can insert or move elements around in the list. If you dont plan to make something very sophisticated just use something existent:
http://www.freebasic.net/forum/viewtopi ... st#p160095

The Data Structure for these lists is something like this: For Memory allocation you use New/Delete and constructors/destructors

Code: Select all

Type _ListItem As ListItem

Type ListItem
	value As Integer
	pPrev As _ListItem
	pNext As _ListItem
End Type

Type List
	pFirst As ListItem	
	count As Integer	 
end type
fxm
Moderator
Posts: 12145
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Deallocate not freeing memory?

Post by fxm »

RockTheSchock is right:

- Before deallocate any string descriptor, you must destroy its character data, otherwise memory leak!

- This is explained in documentation at page STRING:
KeyPgString wrote:..........
Nevertheless if necessary, dynamic allocation may be carefully used by means of the memory allocation functions Allocate, Callocate, Reallocate (see precautions for use) and string pointer (which is a pointer to a string descriptor, not string data). When memory is allocated to hold string descriptors, the string must always be destroyed (setting to "") before deallocate each string descriptor (allowing to deallocate the memory taken up by the string data), otherwise, it is not possible to deallocate it later, and it may induce memory leak in the program continuation.
..........
- Just the two member procedures to be modified in order to well destroy the string character data (without any other improvement over your original progarm):

Code: Select all

sub List.Destroy()

 for i as integer = 0 to items - 1  '***** modification *****
  contents[i] = ""                  '***** modification *****
 next i                             '***** modification *****
 deallocate(contents)
 contents = 0
 items = 0
 
end sub

Code: Select all

sub List.Remove(index as UInteger)

 if items > 0 then
  items -= 1
  contents[index - 1] = ""  '***** modification *****
  for i as Integer = index - 1 to items - 1
   contents[i] = contents[i + 1]
  next
  contents = reallocate(contents, items * SizeOf(String))
 end if
 
end sub
Pinkpuff
Posts: 26
Joined: Jun 10, 2005 18:45

Re: Deallocate not freeing memory?

Post by Pinkpuff »

Thanks for your help.

After implementing those modifications, destroying the first list still only brought it down to about 4.8 Megs but creating the second list only brought it back up to 5.9 again instead of over 10.
fxm
Moderator
Posts: 12145
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Deallocate not freeing memory?

Post by fxm »

I verified your modified program, monitoring at each step the allocated memory to the process, with the Windows (XP) Task Manager:
- Memory Usage
- Virtual Memory
all is OK

Example:
Memory Usage / Virtual Memory
1264 KB / 1380 KB
47232 KB / 47496 KB
1752 KB / 2016 KB
39564 KB / 39824 KB
2280 KB / 2540 KB
pestery
Posts: 493
Joined: Jun 16, 2007 2:00
Location: Australia

Re: Deallocate not freeing memory?

Post by pestery »

Another test program for you, because I was bored :P

Just press (or hold) the plus or minus key to increase or decrease the memory usage. Press any other key to clear any remaining used memory and end the program.

Code: Select all

Const As Integer countmax = 1024 ' Max buffers
Const As Integer buffersize = 8 * (1024 ^ 2) ' 8 MB per buffer

Dim As Any Ptr buffer(1 To countmax)
Dim As Integer count = 0
Dim As ULongInt allocatedmemory = 0

Do
   Print "Allocated memory: " & (allocatedmemory / (1024 ^ 2)) & " MB (" & allocatedmemory & " bytes)"
   Sleep
   Dim As String key = InKey
   While key <> ""
      Select Case LCase(key)

         ' Increase memory usage
         Case "+"
            If count < countmax Then
               count += 1
               buffer(count) = Allocate(buffersize)
               If buffer(count) Then
                  Dim As UByte Ptr buf = buffer(count)
                  ' Add something to the buffer
                  ' The Win7 task manager (and possibly others also) doesn't
                  ' count the allocated buffer if it hasn't been used
                  For i As Integer = 1 To buffersize
                     *buf = i
                     buf += 1
                  Next
                  allocatedmemory += buffersize
               Else
                  count -= 1
               EndIf
            EndIf

            ' Decrease memory usage
         Case "-"
            If count > 0 Then
               If buffer(count) Then
                  DeAllocate buffer(count)
                  buffer(count) = 0
                  allocatedmemory -= buffersize
               EndIf
               count -= 1
            EndIf

            ' Exit program
         Case Else
            Exit Do
      End Select
      key = InKey
   Wend
Loop

' Cleanup
For i As Integer = 1 To countmax
   If buffer(i) Then
      DeAllocate buffer(i)
      buffer(i) = 0
      allocatedmemory -= buffersize
   EndIf
Next
WARNING! If your system has 2GB of ram or less then DO NOT use all of it because your system may freeze. I did that once. It was fun to try but I nearly had to hit the reset button.

If you can compile with the GCC backend then you can get up to 4GB by using:
fbc.exe -gen gcc -Wl --large-address-aware "test.bas"
But again, DO NOT you all your system memory at once. Don't say I didn't warn you :-)
anonymous1337
Posts: 5494
Joined: Sep 12, 2005 20:06
Location: California

Re: Deallocate not freeing memory?

Post by anonymous1337 »

I'd like to chime in for a second. New/Delete support ctor/dtor calls. When you are manually allocating/deallocating memory, you don't get that. I recommend using raw manual memory allocation only for very primitive data structures because of this, or implementing initialize/cleanup methods which are referenced by ctors/dtors.
Last edited by anonymous1337 on Aug 28, 2013 17:39, edited 1 time in total.
fxm
Moderator
Posts: 12145
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Deallocate not freeing memory?

Post by fxm »

And in addition, by using New/Delete (Byte) in the pestery's program, writing in allocated memory (to modify the memory usage field in task manager) is useless because the implicit constructor does this automatically.

pestery's program so modified:

Code: Select all

Const As Integer countmax = 1024 ' Max buffers
Const As Integer buffersize = 8 * (1024 ^ 2) ' 8 MB per buffer

Dim As Byte Ptr buffer(1 To countmax)
Dim As Integer count = 0
Dim As ULongInt allocatedmemory = 0

Do
   Print "Allocated memory: " & (allocatedmemory / (1024 ^ 2)) & " MB (" & allocatedmemory & " bytes)"
   Sleep
   Dim As String key = InKey
   While key <> ""
      Select Case LCase(key)

         ' Increase memory usage
         Case "+"
            If count < countmax Then
               count += 1
               buffer(count) = New Byte[buffersize]
               If buffer(count) Then
                  allocatedmemory += buffersize
               Else
                  count -= 1
               EndIf
            EndIf

            ' Decrease memory usage
         Case "-"
            If count > 0 Then
               If buffer(count) Then
                  Delete[] buffer(count)
                  buffer(count) = 0
                  allocatedmemory -= buffersize
               EndIf
               count -= 1
            EndIf

            ' Exit program
         Case Else
            Exit Do
      End Select
      key = InKey
   Wend
Loop

' Cleanup
For i As Integer = 1 To countmax
   If buffer(i) Then
      Delete[] buffer(i)
      buffer(i) = 0
      allocatedmemory -= buffersize
   EndIf
Next
Post Reply