Suppose I have some simple struct
like this:
public struct WeightedInt {
public int value;
public double weight;
}
Then let's say I have a collection of instances of this structure:
List<WeightedInt> weightedInts = new List<WeightedInt>();
As I understand value types versus reference types, value types are allocated on the stack, so a value type object is cleared from memory once the function instantiating said object terminates. This means that in the following code:
void AddWeightedIntToList(int value, double weight) {
WeightedInt wint = new WeightedInt();
wint.value = value;
wint.weight = weight;
weightedInts.Add(wint);
}
a copy of the local variable wint
is added to weightedInts
whereas the local variable itself is removed from memory after AddWeightedIntToList
is completed.
First of all: is this correct?
Secondly, where does this copy of wint
get stored? It can't be on the stack, since then it would be gone once the function completed (right?). Does this mean that the copy is stored on the heap along with weightedInts
? And is it garbage collected after being removed, as if it were an instance of a reference type?
It's certainly possible that this question is answered in an article somewhere, in which case, a link to that article would be a totally acceptable answer. I just haven't had any luck finding it.
First of all: is this correct?
Yes. The original is "gone" once the scope ends.
Secondly, where does this copy of wint get stored? It can't be on the stack, since then it would be gone once the function completed (right?). Does this mean that the copy is stored on the heap along with weightedInts? And is it garbage collected after being removed, as if it were an instance of a reference type?
Your instance of List<WeightedInt>
creates an array on the heap. You're assigning a portion of that array a copy of your value type when you "add" it to the list. The value is saved on the heap, as part of an array (internal to the List class).
When your weightedInts member goes out of scope, it will become unrooted, and eligible to be garbage collected. At some point after that, the GC will run, and will release the memory associated with it's internal array, thereby freeing the memory associated with your copy of wint.
Edit:
Also, when you call:
weightedInts.Remove(wint);
A few things happen (with List<T>
).
First, the list finds the index of the FIRST instance of your value type that equals wint. It then calls RemoteAt(index).
The RemoveAt(index) method basically marks that the internal size is one smaller, then checks the index you're removing. If it's in the middle of the list, it actually copies ALL of the value type instances up one element using Array.Copy, to "shrink" the list. It then zeros out the memory at the end of the array.
The array itself doesn't shrink, so no memory is released by removing elements. If you want to reclaim this memory (or even make it eligible for freeing by the GC), you need to call List<T>
.TrimExcess().
It is a common misconception that value types are always allocated on the stack.
The example you just showed is a perfect example of value types getting allocated on the heap.
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