DEV Community

Interview question: heap vs stack (C#)

Oleksii Holub on July 11, 2019

Preface Recently I've made myself unemployed and for a few weeks now I'm frequently doing technical interviews as part of looking for a ...
Collapse
 
danstur profile image
danstur • Edited

"Reference types (classes, interfaces, delegates) are always allocated on the heap and never on the stack."
Yes, reference types are generally allocated on the heap but there's no guarantee and clever compilers can and will allocate objects on the stack if they can prove that the reference to the object never escapes (you can read up on Escape analysis).

"Keywords such as ref and out, ref return and ref local (C#7.0), in (C#7.2) allow accessing value types by reference. This means that instead of copying the value, the consuming code will receive a reference to the value instead, be it on a stack or on a heap, as long as the lifetime of that value type is longer than that of consuming code"
Yeah, that's what you'd intuitively think, but sadly that's (generally) not the case. Value types are specified to be immutable, which the compilers has to guarantee. That means if you pass a value type around via ref or similar you'll get a defensive copy. Same thing if you try to call methods on a struct.

This has caused quite the performance problems over the years. Luckily readonly struct was introduced to avoid this problem.

It would also be a good idea to mention that what you're describing about the GC and LOH in particular is an implementation detail and not contractually guaranteed.

Also finalization strategies are also implementation defined. Currently it is not true that the GC stops everything while finalizers are being run (after all we have a dedicated thread for it), which is one reason that makes finalizers so complicated to implement correctly. There is also no guarantee that finalizers won't run on the thread pool instead of a single dedicated thread in the future (so don't rely on finalizers being sequentially executed!). Actually I'm not even sure if .Net Core still has a dedicated thread here.

Collapse
 
tyrrrz profile image
Oleksii Holub

In all fairness, everything about how the memory management works in CLR is an implementation detail, although it doesn't stop interviewers from asking these questions. :)

Value types are specified to be immutable, which the compilers has to guarantee.

You're thinking about ref readonly and in specifically, in which case yes, a defensive copy has to be made because the compiler cannot be sure that the object is not mutated.

Collapse
 
danstur profile image
danstur

"In all fairness, everything about how the memory management works in CLR is an implementation detail, although it doesn't stop interviewers from asking these questions. :)"
I know people who ask these questions in the hope of getting push back, but I'll agree - lots of questionable interview questions out there :)

Also yes you're right, the copy only happens if you use in or access a property of a readonly field that's a struct.

Collapse
 
akashkava profile image
Akash Kava • Edited

The string type deserves a special mention because it's a reference type but it's primarily passed by copy, not by reference. This is the only such type

This is wrong, string is reference type and it is always passed as reference (and reference is a pointer to actual string contents, this pointer is passed as value, pointer is always on stack when passed/returned in method)

However string is designed to be immutable so you cannot modify it (this is why you feel it is copied, but it is not), imagine if you have 100KB of text in string passing it from one method to another would be time consuming. When you run a method like .ToUpper() etc, this is the time a new string is allocated on heap and its reference is sent to you.

Also literal strings are declared in assembly's resources, which is loaded on the heap, string is not copied and it can never be, it would be worst design ever.

reference types are allocated on the heap while value types are allocated on the stack

This statement is correct (with exception of closure), because fields do not constitute as type, class/structure containing them is a type. If experienced developers do not understand true definition of the type then it is certainly wrong place to work !!

Only in case of closure, every captured variable becomes part of a reference stored on heap.

This is the reason, there are local functions, captured value type variables in local functions are not stored on heap.

So I would recommend shorter sentence,

Fields do not constitute as type, all reference types and all value types captured in lambda are on heap and remaining value types are always on stack.

Collapse
 
tyrrrz profile image
Oleksii Holub

This is wrong, string is reference type and it is always passed as reference (and reference is a pointer to actual string contents, this pointer is passed as value, pointer is always on stack when passed/returned in method)
However string is designed to be immutable so you cannot modify it (this is why you feel it is copied, but it is not), imagine if you have 100KB of text in string passing it from one method to another would be time consuming. When you run a method like .ToUpper() etc, this is the time a new string is allocated on heap and its reference is sent to you.

You're completely right, thanks for correcting.

This statement is correct (with exception of closure), because fields do not constitute as type, class/structure containing them is a type. If experienced developers do not understand true definition of the type then it is certainly wrong place to work !!

I'm not sure what your point is here. A reference type (i.e. class) can be declared with a value type field inside of it. The lifespan of the memory allocated for this field cannot be shorter than the lifespan of the memory allocated for the containing type, so both have to be placed on the heap.

Collapse
 
akashkava profile image
Akash Kava

My point is, you cannot use term value type for a field. Field is a member of type. Members belong where ever the containing type exists that's all (this is well known phenomenon).

Thread Thread
 
tyrrrz profile image
Oleksii Holub

My point is, you cannot use term value type for a field. Field is a member of type.

A field is member of a type but also represents an instance of some type as well. The term "value type field" is a field whose type (not the declaring type) is a value type.

For example, see here, you can get the type of a field by getting the value of FieldInfo.FieldType property. You can then check if it's a value type through checking Type.IsValueType property.

Members belong where ever the containing type exists that's all (this is well known phenomenon).

Yes, that's what I said. Hence why saying "value types are allocated on the stack" is not correct, even if you exclude closures.

For example, here's an article by Jon Skeet referencing the subject in the second paragraph.

Thread Thread
 
akashkava profile image
Akash Kava • Edited

Type is something you can always do typeof(x), you can never do type of (field of (class/struct)). Example,

    struct A {
       ? a;
    }
Enter fullscreen mode Exit fullscreen mode

First of all you can never do typeof(A.a) because a is a field of type, it is not a type !

FieldInfo.FieldType is type of field, field is not type. Again, value type is a type, which you can safely do typeof(int), typeof(string), anything that can sit inside typeof expression is a type, field is not type.

Here, A is a type, since it is a struct, it will always be on stack unless captured by lambda. And whatever may be the type field a, A will always be on stack !! Member of a type is not type !! Field/Method/Property all are member of type and allocation will never depend on them. If a is string, it is reference, but string is a type, a is not type, and A will still sit on stack and contents of string will be on heap and a will store reference and entire object will sit on stack.

Thread Thread
 
tyrrrz profile image
Oleksii Holub

You can do A.a.GetType() to get the field type. Field type can be value type. I'm not talking about field being a type.

Collapse
 
selftuaght profile image
SelfTuaght • Edited

I have a question in relevance to the Stack and Heap. I will greatly appreciate any inputs. Thanks.

Question: why is the new operator not used when initializing predefined class type variables(such a variable of type string) or predefined struct type variables(such as a variable of type int)?
For example:
string name = "Richard";
int number = 36;

The new operator was never used when assigning such values to variables name and number. But are such values still considered instances of the respective predefined type.

Reason why I am asking this is because if I were to define a custom class type(say custom class type Person) or custom struct type(say custom struct type Dog). And if I were to declare a variable of such custom class type and such custom struct type, the value assigned to such variables would be an instance of their types created via the use of the new operator.
For example:
Person person1 = new Person( );
Dog dog1 = new Dog( );

Thus, is string type value(string literal) Richard an instance of predefined class type String(alias: string) just like how new Person( ) is an instance of user-defined class type Person?
And is in type value(integral literal) 24 an instance of predefined struct type Int32(alias: int) just like how new Dog( ) is an instance of user-defined struct type Dog?

Collapse
 
xr280xr profile image
xr280xr

It's funny how these topics are such a focus for interview questions. You need to understand the difference between value and reference types, but beyond that, you will rarely need to know any of this. That's the whole point of managed code. There's usually a lot more that should be prioritized ahead of these topics in an interview.

Collapse
 
mcanon profile image
mc-anon

Thanks for writing this! I've been reading about this topic all day and it has been rather confusing to be honest. I have seen read many sources that do assert that the main difference is that value types go to the stack, and reference types to the heap.

This may seem like a silly question (I know, there are no silly questions in programming), but if I declare a global variable of type int, will that be stored on the heap?

Collapse
 
tyrrrz profile image
Oleksii Holub

There are no "global" variables in C#, as everything is part of some class (or struct). But if it's a field in a class then it's most likely going to be on the heap.

Collapse
 
tyrrrz profile image
Oleksii Holub

Thanks, fixed.