Asked  6 Months ago    Answers:  5   Viewed   33 times

I've searched, but I've not understood very well these three concepts. When do I have to use dynamic allocation (in the heap) and what's its real advantage? What are the problems of static and stack? Could I write an entire application without allocating variables in the heap?

I heard that others languages incorporate a "garbage collector" so you don't have to worry about memory. What does the garbage collector do?

What could you do manipulating the memory by yourself that you couldn't do using this garbage collector?

Once someone said to me that with this declaration:

int * asafe=new int;

I have a "pointer to a pointer". What does it mean? It is different of:

asafe=new int;

?

 Answers

42

A similar question was asked, but it didn't ask about statics.

Summary of what static, heap, and stack memory are:

  • A static variable is basically a global variable, even if you cannot access it globally. Usually there is an address for it that is in the executable itself. There is only one copy for the entire program. No matter how many times you go into a function call (or class) (and in how many threads!) the variable is referring to the same memory location.

  • The heap is a bunch of memory that can be used dynamically. If you want 4kb for an object then the dynamic allocator will look through its list of free space in the heap, pick out a 4kb chunk, and give it to you. Generally, the dynamic memory allocator (malloc, new, et c.) starts at the end of memory and works backwards.

  • Explaining how a stack grows and shrinks is a bit outside the scope of this answer, but suffice to say you always add and remove from the end only. Stacks usually start high and grow down to lower addresses. You run out of memory when the stack meets the dynamic allocator somewhere in the middle (but refer to physical versus virtual memory and fragmentation). Multiple threads will require multiple stacks (the process generally reserves a minimum size for the stack).

When you would want to use each one:

  • Statics/globals are useful for memory that you know you will always need and you know that you don't ever want to deallocate. (By the way, embedded environments may be thought of as having only static memory... the stack and heap are part of a known address space shared by a third memory type: the program code. Programs will often do dynamic allocation out of their static memory when they need things like linked lists. But regardless, the static memory itself (the buffer) is not itself "allocated", but rather other objects are allocated out of the memory held by the buffer for this purpose. You can do this in non-embedded as well, and console games will frequently eschew the built in dynamic memory mechanisms in favor of tightly controlling the allocation process by using buffers of preset sizes for all allocations.)

  • Stack variables are useful for when you know that as long as the function is in scope (on the stack somewhere), you will want the variables to remain. Stacks are nice for variables that you need for the code where they are located, but which isn't needed outside that code. They are also really nice for when you are accessing a resource, like a file, and want the resource to automatically go away when you leave that code.

  • Heap allocations (dynamically allocated memory) is useful when you want to be more flexible than the above. Frequently, a function gets called to respond to an event (the user clicks the "create box" button). The proper response may require allocating a new object (a new Box object) that should stick around long after the function is exited, so it can't be on the stack. But you don't know how many boxes you would want at the start of the program, so it can't be a static.

Garbage Collection

I've heard a lot lately about how great Garbage Collectors are, so maybe a bit of a dissenting voice would be helpful.

Garbage Collection is a wonderful mechanism for when performance is not a huge issue. I hear GCs are getting better and more sophisticated, but the fact is, you may be forced to accept a performance penalty (depending upon use case). And if you're lazy, it still may not work properly. At the best of times, Garbage Collectors realize that your memory goes away when it realizes that there are no more references to it (see reference counting). But, if you have an object that refers to itself (possibly by referring to another object which refers back), then reference counting alone will not indicate that the memory can be deleted. In this case, the GC needs to look at the entire reference soup and figure out if there are any islands that are only referred to by themselves. Offhand, I'd guess that to be an O(n^2) operation, but whatever it is, it can get bad if you are at all concerned with performance. (Edit: Martin B points out that it is O(n) for reasonably efficient algorithms. That is still O(n) too much if you are concerned with performance and can deallocate in constant time without garbage collection.)

Personally, when I hear people say that C++ doesn't have garbage collection, my mind tags that as a feature of C++, but I'm probably in the minority. Probably the hardest thing for people to learn about programming in C and C++ are pointers and how to correctly handle their dynamic memory allocations. Some other languages, like Python, would be horrible without GC, so I think it comes down to what you want out of a language. If you want dependable performance, then C++ without garbage collection is the only thing this side of Fortran that I can think of. If you want ease of use and training wheels (to save you from crashing without requiring that you learn "proper" memory management), pick something with a GC. Even if you know how to manage memory well, it will save you time which you can spend optimizing other code. There really isn't much of a performance penalty anymore, but if you really need dependable performance (and the ability to know exactly what is going on, when, under the covers) then I'd stick with C++. There is a reason that every major game engine that I've ever heard of is in C++ (if not C or assembly). Python, et al are fine for scripting, but not the main game engine.

Tuesday, June 1, 2021
 
Saxophlutist
answered 6 Months ago
20

Automatic tells me something about the lifetime of an object: specifically that it is bound automatically to the enclosing scope, and will be destroyed automatically when that scope exits.

Dynamic tells me that the lifetime of an object is not controlled automatically by the compiler, but is under my direct control.

Stack is an overloaded name for a type of container, and for the related popular instruction pointer protocol supported by common call and ret instructions. It doesn't tell me anything about the lifetime of an object, except through a historical association to object lifetimes in C, due to popular stack frame conventions. Note also that in some implementations, thread-local storage is on the stack of a thread, but is not limited to the scope of any single function.

Heap is again an overloaded name, indicating either a type of sorted container or a free-store management system. This is not the only free store available on all systems, and nor does it tell me anything concrete about the lifetime of an object allocated with new.

Tuesday, June 1, 2021
 
jerrygarciuh
answered 6 Months ago
48

3 rules of thumb:

  1. Objects are stored on the heap. These include instances of reference-types and boxed value-types.
  2. Local variables and parameters are stored on the stack. For local value-types, this means that the value itself is stored on the stack. For local reference-types, only the reference will be on the stack (Edit: Exceptions noted by Eric Lippert - value-type locals closed over outer variables, iterator-block value-types).
  3. Fields are stored where the containing instance is located. For example, a value-type field of a class will be stored on the heap. The reference-part of a reference-type field of a struct declared as a local stored on the stack will also be on the stack.
Wednesday, August 4, 2021
 
ysquared
answered 4 Months ago
63

It wouldn't cause a leak - the GC can handle circular references with no problems.

However, it would mean that the publisher would effectively have a reference to the subscriber, so the subscriber couldn't be garbage collected until either the publisher is eligible for GC, or it unsubscribes from the event.

Thursday, August 12, 2021
 
Eddas
answered 4 Months ago
75

What do you mean by "don't get the advantages of GC"? Methods aren't garbage collected - instances are.

Virtual methods are slightly slower than non-virtual ones, and I guess there's that pesky null check before any instance method, but it's not significant. Go with the most appropriate design.

Static methods are a pain for testing though - for instance, if you authenticate in method Foo() by calling some static method, then when you're testing Foo() you can't make it just call a mock authenticator (unless the static method itself lets you do that). If you give the original instance of whatever you're testing a mock implementation of some interface containing an Authenticate() method, however, you can make it behave however you want.

EDIT: In this case, it sounds like what you really need is an instance method on your Point type to calculate the distance between two points ("this" one and another) - or potentially a static factory method on the Distance type.

Saturday, November 6, 2021
 
skrilled
answered 3 Weeks ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share