21

From an SO answer1 about Heap and Stack, it raised me a question: Why it is important to know where the variables are allocated?

At another answer someone pointed that the stack is faster. Is this the only implication? Could someone give a code example where a simple allocation location change could solve a problem (eg. performance)?

Note that this question is .NET specific

1 the question is removed from SO.

trincot
  • 317,000
  • 35
  • 244
  • 286
Jader Dias
  • 88,211
  • 155
  • 421
  • 625
  • 1
    Note to future Googlers, the **first link** in this c# question is **broken** (as indicated) and the **second link** is about `stack allocation in general` and **not specific to .NET**. –  Mar 29 '15 at 02:06
  • As pointed out by Warren P's with his 66 helpful votes(at the time of writing): _"[Is it snarky to point out that it is the success of modern "managed code" languages that awareness of stack and heap are no longer necessary (much) for C# and Java developers, because those of us who have to debug things that we did wrong in C and C++ needed to understand the model so we can fix our code when it breaks. In other words, 'ignorance is power', because the abstraction works as designed, allowing me to work on a virtual machine that is easier to use than the real one.](http://tinyurl.com/4xueee6)"_ –  Mar 29 '15 at 02:12
  • Never mentioned in the current answers: Values allocated on the stack may have to be relocated on the heap, and back many times. That's what boxing and unboxing does, e.g. when using non generic collections (collections of `object` stored in the heap) when items are cast as e.g. `int` (stored in the stack), then reboxed (e.g. because they are passed as `object` in a method). The cost is significant. It may be reduced by designing variables and method arguments not requiring boxing/unboxing. [More](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/boxing-and-unboxing). – mins Apr 05 '19 at 18:29

7 Answers7

18

So long as you know what the semantics are, the only consequences of stack vs heap are in terms of making sure you don't overflow the stack, and being aware that there's a cost associated with garbage collecting the heap.

For instance, the JIT could notice that a newly created object was never used outside the current method (the reference could never escape elsewhere) and allocate it on the stack. It doesn't do that at the moment, but it would be a legal thing to do.

Likewise the C# compiler could decide to allocate all local variables on the heap - the stack would just contain a reference to an instance of MyMethodLocalVariables and all variable access would be implemented via that. (In fact, variables captured by delegates or iterator blocks already have this sort of behaviour.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • The link is broken. Does anyone know where the article could still be read? – JamesHoux Sep 13 '19 at 01:35
  • @JamesHoux: I'm afraid I don't think I've got that to hand any more. Will remove that paragraph. But it's likely to be very similar to https://blogs.msdn.microsoft.com/ericlippert/2009/04/27/the-stack-is-an-implementation-detail-part-one/ – Jon Skeet Sep 13 '19 at 05:36
5

(edit: My original answer contained the oversimplification "structs are allocated on the stack" and confused stack-vs-heap and value-vs-reference concerns a bit, because they are coupled in C#.)

Whether objects live on the stack or not is an implementation detail which is not very important. Jon has already explained this well. When choosing between using a class and struct, it is more important to realize that reference types work differently than value types. Take the following simple class as an example:

public class Foo
{
   public int X = 0;
}

Now consider the following code:

Foo foo = new Foo();
Foo foo2 = foo;
foo2.X = 1;

In this example, foo and foo2 are references to the same object. Setting X on foo2 will also affect foo1. If we change the Foo class to a struct then this is no longer the case. This is because structs are not accessed through references. Assigning foo2 will actually make a copy.

One of the reasons for putting stuff on the stack is that the garbage collector does not have to clean it up. You typically should not worry about such things; just use classes! Modern garbage collectors do a pretty good job. Some modern virtual machines (like java 1.6) can even determine whether it is safe to allocate objects on the stack even if they are not value types.

Wim Coenen
  • 66,094
  • 13
  • 157
  • 251
  • 2
    While your example and explanation are correct, you've demonstrated a counterexample to your first sentence. What is "Foo.X"? An int, which is a value type - and yet it always lives on the heap. The claim of "classes are heap allocated, structs are stack allocated" is an oversimplification. – Jon Skeet Jan 25 '09 at 07:41
  • 1
    A better wording would be to say that value types are allocated where they're declared. If they're declared as local variables in a function, they're on the stack. If they're declared as members in a class, they're on the heap, as part of that class. – jalf Jan 25 '09 at 09:45
  • 1
    I will not vote up this answer because it mixes reference vs value implications with heap vs stack implications. – Jader Dias Jan 25 '09 at 12:40
  • Thanks for the correction, it has triggered an "Aha!" moment. I now realize that stack-vs-heap is a relatively unimportant implementation detail in C#, and that value-vs-reference is the real issue here. I should have explained it that way. – Wim Coenen Jan 25 '09 at 14:00
  • Someone might correct me on this, but aren't class members/properties also stored on the heap? e.g. person.Age = 30; is on the heap – Chris S Jan 25 '09 at 20:32
  • @chris: Yes, that's what Jon is saying in the first comment, and why I added a note "look at the comments". What's the best practice here, should I go in and fix the post proper instead of referring to the comments? – Wim Coenen Jan 26 '09 at 00:39
  • I went ahead and fixed it proper since people appear to skip over the "look at the comments" note. – Wim Coenen Jan 26 '09 at 00:51
4

In .NET there's little to discuss as it is not the user of a type who decides where to allocate instances.

Reference types are always allocated on the heap. Value types are per default allocated on the stack. The exception is if the value type is part of a reference type in which case it is allocated on the heap along with the reference type. I.e. the designer of a type makes this decision on behalf of the users.

In languages such as C or C++ the user can decide where data is allocated and for some special cases it may be significantly faster to allocate from the stack compared to allocating from the heap.

This has to do with how heap allocations are handled for C / C++. In fact heap allocation is pretty fast in .NET (except when it triggers a garbage collect), so even if you could decide where to allocate, my guess is that the difference would not be significant.

However, since the heap is garbage collected and the stack is not, obviously you would see some differences in certain cases, but it is hardly relevant given the fact that you don't really have a choice in .NET.

Brian Rasmussen
  • 114,645
  • 34
  • 221
  • 317
  • Thanks for this fundamental remark: *In .NET there's little to discuss as it is not the user of a type who decides where to allocate instances, while In languages such as C or C++, the user can decide where data is allocated and for some special cases it may be significantly faster to allocate from the stack compared to allocating from the heap.* – Veverke Jul 18 '16 at 12:46
3

I think the simplest reason is that if it is in the Heap the the garbage collection needs to deal with the variable once it is no longer needed. When on a Stack, the variable is dismissed with whatever was using it, such as a method that instantiated it.

DavGarcia
  • 18,540
  • 14
  • 58
  • 96
3

In my opinion knowing about the differences between the stack and heap and how things are allocated on it can be very helpful when you really start thinking about performance of your application. The following questions make it essential to understand the differences: What do you think is faster and more efficient for .NET to access? - Stack or Heap. In what scenarios .NET may place a value type of the heap?

Ankit Dass
  • 521
  • 5
  • 7
1

Contrary to popular belief, there isn’t that much of a difference between stacks and heaps in a .NET process. Stacks and heaps are nothing more than ranges of addresses in virtual memory, and there is no inherent advantage in the range of addresses reserved to the stack of a particular thread compared to the range of addresses reserved for the managed heap. Accessing a memory location on the heap is neither faster nor slower than accessing a memory location on the stack. There are several considerations that might,in certain cases, support the claim that memory access to stack locations is faster, overall, than memory access to heap locations. Among them:

  1. On the stack, temporal allocation locality (allocations made close together in time) implies spatial locality (storage that is close together in space). In turn, when temporal allocation locality implies temporal access locality (objects allocated together are accessed together), the sequential stack storage tends to perform better with respect to CPU caches and operating system paging systems.
  2. Memory density on the stack tends to be higher than on the heap because of the reference type overhead. Higher memory density often leads to better performance, e.g., because more objects fit in the CPU cache.
  3. Thread stacks tend to be fairly small – the default maximum stack size on Windows is 1MB, and most threads tend to actually use only a few stack pages. On modern systems,the stacks of all application threads can fit into the CPU cache, making typical stack object access extremely fast. (Entire heaps, on the other hand, rarely fit into CPU caches.)

With that said, you should not be moving all your allocations to the stack! Thread stacks on Windows are limited, and it is easy to exhaust the stack by applying injudicious recursion and large stack allocations.

Ali Bayat
  • 3,561
  • 2
  • 42
  • 43
  • 1
    This makes perfect sense, and I really appreciate you taking the time to provide such a detailed response! – Elham Azadfar Jun 21 '18 at 14:49
  • 1
    Part of this answer is incomplete. The answer says "...the default maximum stack size on Windows is 1MB". This is true *only* for 32-bit processes. A 64-bit Windows process has a default stack size of 4MB. – Dave Black Mar 13 '19 at 20:18
1

I played a lot with a different benchmarks on stack and heap and I would conclude in following:

Similar performance for

  • small apps (not too many objects on heap)
  • objects of size < 1KB

Sligther better stack performance for (1x - 5x faster)

  • Objects of size 1Kb to 100KB

Much better performance for (up to 100x faster or even more)

  • Large number of objects
  • Big memory presure - a lot allocations per seconds and full memory
  • large objects 10KB - 3MB (I suppose x64 systems)
  • XBox (slow garbage collector)

The best aproach is array pooling. It is fast as stack, but you don't have such limitation like with stack.

Another implication of using stack is that it is thread-safe by desing.

Default memory limit for stack on x64 Windows is 4MB. So you will be safe with allocating not more than 3MB.

Tomas Kubes
  • 23,880
  • 18
  • 111
  • 148
  • Here is an excellent article on the use of Array Pools (as suggested in the answer): https://adamsitnik.com/Array-Pool/ – JamesHoux Sep 13 '19 at 01:51
  • For clarification to other readers of this answer: the performance differences shown are not due to "the stack vs heap" per se. The cost of larger objects is due to the garbage collector promoting gen0 to gen1 and gen2 objects. In the case of small objects, the objects are probably all remaining as gen0, which is making their performance comparable to the stack. Author Tomas Kubes probably recognizes this... as he suggests using array pooling, which completely bypasses GC costs and makes heap performance same as stack. – JamesHoux Sep 13 '19 at 01:55
  • Also, for anyone interested in Array Pooling when dealing with very large object heaps, have a look at Piles. Piles are a way to represent objects in a single gigantic array, making them completely invisible to the garbage collector and giving incredible performance. – JamesHoux Sep 13 '19 at 01:59