Author Topic: Does MEMFREE need to be called if the Memory Block is an existing variable?  (Read 3361 times)

0 Members and 1 Guest are viewing this topic.

Offline EricE

  • Forum Regular
  • Posts: 114
    • View Profile
Does _MEMFREE have to be called if the memory block is an already existing variable?
Consider the following program, the variable x is already DIM as an INTEGER.
Does _MEMFREE have to be called to release the Memory Block?
In this program the variable x still exists after _MEMFREE is called.
So what actually is the memory block released in this program?

Another way of asking the question.
If we have the following:

DIM m AS _MEM
DIM x AS INTEGER

Is any new memory allocated when we write the following?

m=_MEM(x)

Code: QB64: [Select]
  1.  
  2. m = _MEM(x)
  3.  
  4. _MEMPUT m, m.OFFSET, 1 AS INTEGER
  5.  
  6.  
  7.  

Marked as best answer by EricE on March 31, 2020, 02:55:36 pm

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
There's a few bytes allocated to hold the mem type information with each DIM m AS _MEM statement, but no new memory is allocated anywhere unless _MEMNEW is used. 

M = _MEM(X) basically just gives you a pointer to X, so you don't have to obsess quite so much about making certain to use _MEMFREE.

M =_MEMNEW(8) -- now this allocates new memory for M each time it's called, so don't forget your_MEMFREE with it.
« Last Edit: March 31, 2020, 12:07:17 pm by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline EricE

  • Forum Regular
  • Posts: 114
    • View Profile
Hi Steve,
Thank you for your reply.
I wrote this program to see what happens if _MEMFREE is not called.
It is currently running on my PC and I am going to leave it going while I look go to WalMart to look for supplies.
Windows task manager says it is using 59.1MB of memory. Will check what it is using when I return home.

Code: QB64: [Select]
  1. i& = 1
  2.  
  3.     IF i& MOD 1000 = 0 THEN PRINT i&
  4.     MemSub
  5.     i& = i& + 1
  6.  
  7. SUB MemSub
  8.     ' Very large array, 100,000,000 elements, each element 8 bytes in size.
  9.     DIM a(10000000) AS _INTEGER64
  10.     DIM mema AS _MEM
  11.  
  12.     mema = _MEM(a)
  13.     ' do something with array
  14.     _MEMFILL mema, mema.OFFSET, mema.SIZE, &HFFFFFFFFFFFFFFFF AS LONG
  15.  

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
It shouldn't do anything.  When the sub exits, your array is freed and so is the mem block, automatically.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline EricE

  • Forum Regular
  • Posts: 114
    • View Profile
Just checked the program now at 9:35am, it has been running since about 8:18am, so for 1 hour, 17 minutes, output counter is at 2816000, and the Task Manager memory usage is at 30.7MB!
The drop is probably due to Windows management.
I will continue to let it run.

EDIT to add:
I let the program run for some hours, no memory leaks were observed. In fact memory usage appeared to go down!
This really was a benign test since the program really wasn't expected to not release memory.
« Last Edit: March 31, 2020, 06:54:59 pm by EricE »

Offline EricE

  • Forum Regular
  • Posts: 114
    • View Profile
On the QB64 wiki page for the  MEM (function)
http://www.qb64.org/wiki/MEM_(function)
this example is given:

Code: QB64: [Select]
  1. DIM SHARED Saved(3)
  2.  
  3. m(1) = _MEM(x)
  4. m(2) = _MEM(y)
  5. m(3) = _MEM(z)
  6.  
  7. x = 3: y = 5: z = 8
  8. PRINT x, y, z
  9. Save x, y, z
  10. x = 30: y = 50: z = 80
  11. PRINT x, y, z
  12.  
  13. RestoreIt
  14. PRINT x, y, z
  15.  
  16.  
  17. SUB Save (n1, n2, n3)
  18. Saved(1) = n1
  19. Saved(2) = n2
  20. Saved(3) = n3
  21.  
  22. SUB RestoreIt
  23. _MEMPUT m(1), m(1).OFFSET, Saved(1)
  24. _MEMPUT m(2), m(2).OFFSET, Saved(2)
  25. _MEMPUT m(3), m(3).OFFSET, Saved(3)
  26. END SUB  

Can we say definitely that the three _MEMFREE calls are not necessary in terms of freeing up memory?
The compiler has already allocated memory for the variables x, y, and z, and these variables continue to persist after the calls to the _MEMFREE function, so their memory blocks have not been freed.

Offline EricE

  • Forum Regular
  • Posts: 114
    • View Profile
Streve wrote:
Quote
There's a few bytes allocated to hold the mem type information with each DIM m AS _MEM statement, but no new memory is allocated anywhere unless _MEMNEW is used.

So how much memory do _MEM types take up?

This program uses LEN to find out.
Code: QB64: [Select]
  1. DIM ofset AS _OFFSET
  2.  
  3. $IF 32BIT THEN
  4.     PRINT "32-BIT VALUES"
  5.  
  6. $IF 64BIT THEN
  7.     PRINT "64-BIT VALUES"
  8.  
  9. PRINT "LEN(_OFFSET) = ", LEN(ofset)
  10. PRINT "LEN(_MEM)    = ", LEN(m)
  11.  

Running this program using the QB64 32-bit and 64-bit compilers gives the following data:

32-BIT VALUES
  • LEN(_OFFSET) =  4
  • LEN(_MEM)      = 32
64-BIT VALUES
  • LEN(_OFFSET) =  8
  • LEN(_MEM)      = 52


Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
If you're really curious, you can see what a mem_block structure contains behind the scenes in common.cpp:

Code: C++: [Select]
  1.  
  2.   struct mem_block{
  3.         ptrszint offset;
  4.         ptrszint size;
  5.         int64 lock_id;//64-bit key, must be present at lock's offset or memory region is invalid
  6.         ptrszint lock_offset;//pointer to lock
  7.         ptrszint type;
  8.         /*
  9.             memorytype (4 bytes, but only the first used, for flags):
  10.             1 integer values
  11.             2 unsigned (set in conjunction with integer)
  12.             4 floating point values
  13.             8 char string(s) 'element-size is the memory size of 1 string
  14.         */
  15.         ptrszint elementsize;
  16.         int32 image;
  17.     };

ptrszint is 4 bytes on 32-bit systems, 8 bytes on 64-bit systems to hold offset/pointer values.
int32 is basically a LONG, at 4 bytes.
int64 is an INTEGER64, and 8 bytes.

Which, when totaled up, gives you the 32 or 52 which you found totaled up with LEN() in your test code.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline EricE

  • Forum Regular
  • Posts: 114
    • View Profile
Now that is very interesting!
A look under the QB64 hood.
Thank you Steve.