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 ...
For further actions, you may consider blocking this person and/or reporting abuse
"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.
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. :)
You're thinking about
ref readonly
andin
specifically, in which case yes, a defensive copy has to be made because the compiler cannot be sure that the object is not mutated."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.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.
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,
You're completely right, thanks for correcting.
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.
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).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.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.
Type is something you can always do
typeof(x)
, you can never do type of (field of (class/struct)). Example,First of all you can never do
typeof(A.a)
becausea
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 dotypeof(int), typeof(string)
, anything that can sit insidetypeof
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 fielda
,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. Ifa
is string, it is reference, but string is a type,a
is not type, andA
will still sit on stack and contents of string will be on heap anda
will store reference and entire object will sit on stack.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.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?
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.
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?
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.
Thanks, fixed.