As library docs say CString
created with newCString
must be freed with free
function. I have been expecting that when CString
is created it would take some memory and when it is released with free
memory usage would go down, but it didn't! Here is example code:
module Main where
import Foreign
import Foreign.C.String
import System.IO
wait = do
putStr "Press enter" >> hFlush stdout
_ <- getLine
return ()
main = do
let s = concat $ replicate 1000000 ['0'..'9']
cs <- newCString s
cs `seq` wait -- (1)
free cs
wait -- (2)
When program stopped at (1), htop
program showed that memory usage is somewhere around 410M - this is OK. I press enter and the program stops at line (2), but memory usage is still 410M despite cs
has been free
d!
How is this possible? Similar program written in C behaves as it should. What am I missing here?
You must use delete operator to deallocate memory when it is allocated by new operator.
The free() function is used to free the memory which is allocated previously using malloc() or calloc(). Syntax: free(mem_ptr);
If you attempt to read or write to memory that was previously freed, the result will be a conflict and the program will generate a memory error. For example, if a program calls the free() function for a particular block and then continues to use that block, it will create a reuse problem when a malloc() call is made.
std::string is just a normal class1, so the usual rules apply. If you allocate std::string objects on the stack, as globals, as class members, ... you don't need to do anything special, when they go out of scope their destructor is called, and it takes care of freeing the memory used for the string automatically.
The issue is that free
just indicates to the garbage collector that it can now collect the string. That doesn't actually force the garbage collector to run though -- it just indicates that the CString is now garbage. It is still up to the GC to decide when to run, based on heap pressure heuristics.
You can force a major collection by calling performGC
straight after the call to free
, which immediately reduces the memory to 5M or so.
E.g. this program:
import Foreign
import Foreign.C.String
import System.IO
import System.Mem
wait = do
putStr "Press enter" >> hFlush stdout
_ <- getLine
return ()
main = do
let s = concat $ replicate 1000000 ['0'..'9']
cs <- newCString s
cs `seq` wait -- (1)
free cs
performGC
wait -- (2)
Behaves as expected, with the following memory profile - the first red dot is the call to performGC
, immediately deallocating the string. The program then hovers around 5M until terminated.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With