views:

239

answers:

2

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.

+5  A: 

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().

Reed Copsey
From what you're saying, I can conclude that the value added to `weightedInts` will be garbage collected once `weightedInts` goes out of scope. But what remains unclear to me is what happens to it if I call `weightedInts.Remove(wint);` -- does it remain lying around there until `weightedInts` is garbage collected?
Dan Tao
No. I'll edit to clarify.
Reed Copsey
So a list of ints would be stored in the heap and the only allocation on the stack would be the pointer to the heap for the array?
Ty
Well, it's actually a pointer to the List<T>, which internally, contains a pointer to an array and an integer value used for tracking, but yes, that's the general idea. The array is allocated on the heap, as is the array reference inside of the List<T>.
Reed Copsey
@Dan, your weightedInts is not garbage collected when it goes oiut of scope, it's garbage collected _at sometime_ after it has gone out of scope. (Which in most cases does not make a differance)
Rune FS
@Reed First of all very nice answer. but strictly speaking I don't think it's not a pointer to the List that get's stored on the heap it's a reference (and in the MS implementation they are based on pointers as I recall but they don't need to be and are not required to be)
Rune FS
@Rune: The heap, in general, is an implementation detail. References, even, aren't a .NET native thing - the closest "native" thing is a GCHandle. MS's implementation uses pointers to manage memory within the GC, since that's just a reference to a location in memory.
Reed Copsey
"When your weightedInts member goes out of scope, it will become unrooted, and eligible to be garbage collected" - as a value type it won't need to be garbage collected, and won't become unrooted as it wasn't being tracked in the first place.
Joe
@Joe: When weightedInts goes out of scope, the ARRAY of value types gets unrooted, and is available for GC. You're not storing a single value type, but rather an array (of value types), which is, itself, a reference type on the heap.
Reed Copsey
It's funny; what seemed so puzzling to me at the time I posted this question now seems clear as day after just a little over a month's worth more of experience. Your answer was very helpful.
Dan Tao
+1  A: 

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.

Dan Herbert