When is a mutable state value freed from the heap?

The F # WikiBook has the following code snippet under the Encapsulating Mutable State section.

> let incr =
    let counter = ref 0
    fun () ->
        counter := !counter + 1
        !counter;;

val incr : (unit -> int)

> incr();;
val it : int = 1

> incr();;
val it : int = 2

> incr();;
val it : int = 3

      

At first, it was easy to grasp the fact that when you change counter

, the mutable value changes counter

every time incr

.

But after thinking about it for a while, I couldn't figure out when it is counter

freed from the heap, nor how is counter

it still referencing the previous value before incrementing it. How does counter

one that lives within a incr

function survive through multiple function calls?

So the main questions are:

  • When is counter

    heap freed?
  • Not counter

    a memory leak?
0


source to share


3 answers


The distinction between "lexical scope" (where the name makes sense in the text of the program) and "lifespan" (the duration of execution between when an object is created and destroyed) can sometimes be confusing, as often the two are highly correlated. However, the method demonstrated in this example is common in functional languages: you give implementation details a small lexical scope (which hides implementation details from callers), but extend its lifetime by capturing it in a closure (so its lifetime becomes the lifetime of the enclosing object - in this case function "incr"). This is the usual way to do encapsulation in functional programming (as opposed to the usual public / private encapsulation method in classes in object-oriented programming).

Now, in this particular example, it looks like "incr" is a top-level function, which means that its value lasts for the life of the program (or an interactive session when typed into fsi.exe). You could call this a "leak," but it depends on intent. If you have a unique ID counter that you need throughout your entire program, then you will have to store that counter variable somewhere to keep it going throughout your entire program. So it is either a "leak" or a "design feature" depending on how incr is used (would you need to use this function for the rest of the program?). In any case, the key point here is that "incr" contains memory resources, so,if you don't need these resources forever, you should arrange for the closure referenced by "incr" to become unavailable when you no longer need it. This can usually be due to the locality of some other function, for example



let MyComplicatedFuncThatNeedsALocalCounter args =
    let incr = 
        // as before
    // other code that uses incr
    // return some result that does not capture incr

      

+4


source


In this case, it incr

is a top-level function (implemented as a static field, if I'm not mistaken.) It contains a closure, which in turn has a reference to that named ref cell counter

. As long as this closure exists, the cell ref

is kept in memory.



Now this top-level binding will never get garbage collected as it is a static readonly field. (in C # terms). If you do, however, have closures with a limited lifetime (either locally or in an object), the cell ref

will be freed when the closure is garbage collected.

+3


source


Counter

freed from the heap when incr is no longer available. This is not a memory leak due to garbage collection.

+2


source







All Articles