10

Let's go back to the basics. Frankly, I have never used New and Dispose functions before. However, after I read the New() documentation and the included examples on the Embarcadero Technologies's website and the Delphi Basics explanation of New(), it leaves questions in my head:

What are the advantages of using System.New() instead of a local variable, other than just spare a tiny amount of memory?

Common code examples for New() are more or less as follows:

  var
      pCustRec : ^TCustomer;
  begin
      New(pCustRec);
      pCustRec^.Name := 'Her indoors';
      pCustRec^.Age  := 55;
      Dispose(pCustRec);
  end;

In what circumstances is the above code more appropriate than the code below?

  var
      CustRec : TCustomer;
  begin
      CustRec.Name := 'Her indoors';
      CustRec.Age  := 55;
  end;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Astaroth
  • 2,241
  • 18
  • 35
  • 1
    Instead of `New/Dispose`, use [dynamic array](http://docwiki.embarcadero.com/RADStudio/XE3/en/Structured_Types#Dynamic_Arrays) constructs to allocate space on the heap. Theay are reference counted and are disposed automatically when they go out of scope. – LU RD Jan 09 '13 at 06:59
  • 1
    So you would create an array with one element? Not really clean code. – alzaimar Jan 09 '13 at 07:28
  • @alzaimar, with one element the most convenient way is to use the stack anyway. – LU RD Jan 09 '13 at 09:18
  • @LURD: In this special case (and other simple structures) YES. – alzaimar Jan 09 '13 at 20:48

6 Answers6

11

If you can use a local variable, do so. That's a rule with practically no exceptions. This results in the cleanest and most efficient code.

If you need to allocate on the heap, use dynamic arrays, GetMem or New. Use New when allocating a record.

Examples of being unable to use the stack include structures whose size are not known at compile time, or very large structures. But for records, which are the primary use case for New, these concerns seldom apply.

So, if you are faced with a choice of stack vs heap for a record, invariably the stack is the correct choice.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • This is a straightforward answer and judges the choices firmly. I like this one. Thanks. – Astaroth Jan 09 '13 at 10:32
  • 4
    Another example for using pointers (i.e. for using New) is growing structures like linked lists, trees, etc. Each element is allocated with New(). FWIW, New with a properly typed pointer is preferrable to GetMem, as New will properly initialize the memory and Dispose will properly finalize it. – Rudy Velthuis Jan 09 '13 at 14:34
  • 1
    +1, finally I can see a situation when `New()` comes to the rescue. Thanks. – Astaroth Jan 09 '13 at 14:48
8

From a different perspective:

Both can suffer from buffer overflow and can be exploited.

If a local variable overflows, you get stack corruption.

If a heap variable overflows, you get heap corruption.

Some say that stack corruptions are easier to exploit than heap corruptions, but that is not true in general.

Note there are various mechanisms in operating systems, processor architectures, libraries and languages that try to help preventing these kinds of exploits.

For instance there is DEP (Data Execution Prevention), ASLR (Address Space Layout Randomization) and more are mentioned at Wikipedia.

Jeroen Wiert Pluimers
  • 23,965
  • 9
  • 74
  • 154
  • 2
    You can run FastMM4 in full debug mode to track such overflows. But how may you trigger a buffer overflow with fixed size records, and no pointer arithmetic nor indexed access, as the OP asked? With stack-allocated static variables or New/Dispose, you just can't have such exploits. IMHO your answer is just out of scope, here. – Arnaud Bouchez Jan 09 '13 at 16:24
  • Usually it starts simple, then code grows, and finally reality overtakes. Consider my answer as a preemptive warning, hence the "from a different perspective". – Jeroen Wiert Pluimers Jan 09 '13 at 19:40
7

A local static variable reserves space on the limited stack. Allocated memory is located on the heap, which is basically all memory available.

As mentioned, the stack space is limited, so you should avoid large local variables and also large parameters which are passed by value (absence of var/const in the parameter declaration).

A word on memory usage:
1. Simple types (integer, char, string, double etc.) are located directly on the stack. The amount of bytes used can be determined by the sizeof(variable) function.
2. The same applies to record variables and arrays. 3. Pointers and Objects require 4/8 bytes.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
alzaimar
  • 4,572
  • 1
  • 16
  • 30
  • The default stack size is 1 MB (and it could be increased if needed). I think 1 MB is more than enough except for exceptional conditions. However, +1 for any other useful information. – Astaroth Jan 09 '13 at 10:23
  • 1
    Well, if you declare local arrays, records an all this old fashioned stuff and/or pass such parameters as value, your 1MB will shrink to zero quite fast, especially when using recursive calls. – alzaimar Jan 09 '13 at 20:47
  • 1
    cases of infinite/excessively deep recursions are rare, but if that is the case, just write a nonrecursive solution for the problem. – Astaroth Jan 10 '13 at 03:04
  • 2
    No. Use pointers, it is much easier to implement ;-) I will not create a complicated iterative solution just to be able to make use of large local variables. I will rather use dynamic local variables (or simply objects). I could give you enough examples from real life (business) applications where the use of large(!) local variables would blow up the stack. – alzaimar Jan 10 '13 at 06:35
4

Every object (that is, class instances) is always allocated on the heap.

Value structures (simple numerical types, records containing only those types) can be allocated on the heap.

Dynamic arrays and strings content are always allocated on the heap. Only the reference pointer can be allocated on the stack. If you write:

  function MyFunc;
  var s: string;
  ...

Here, 4/8 bytes are allocated on the stack, but the string content (the text characters) will always be allocated on the heap.

So using New()/Dispose() is of poor benefit. If it contains no reference-counted types, you may use GetMem()/FreeMem() instead, since there is no internal pointer to set to zero.

The main drawback of New() or Dispose() is that if an exception occur, you need to use a try...finally block:

  var
    pCustRec : ^TCustomer;
  begin
    New(pCustRec);
    try
      pCustRec^.Name := 'Her indoors';
      pCustRec^.Age  := 55;
    finally
      Dispose(pCustRec);
    end;
  end;

Whereas allocating on the stack let the compiler do it for you, in an hidden manner:

  var
    CustRec : TCustomer;
  begin // here a try... is generated
    CustRec.Name := 'Her indoors';
    CustRec.Age  := 55;
  end;  // here a finally + CustRec cleaning is generated

That's why I almost never use New()/Dispose(), but allocate on stack, or even better within a class. 2

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Arnaud Bouchez
  • 42,305
  • 3
  • 71
  • 159
  • "Value structures (simple numerical types, records containing only those types) can be allocated on the head." - shouldn't this be "... on the stack" ? – Frank Schmitt Jan 09 '13 at 07:16
  • Wasting the stacks space is a bad habit. -1 for that. Recommending classes/objects is a good habit. +1 for that. Using the term 'need' in conjunction with a try-finaly-block not a good suggestion, -1 for that (phew!). Also, mentioning the lack of reference-counted types in a delphi related question does not really fit, as delphi itself does not care much about reference counting (except for interfaces and strings). In general: You should always use a try-finally block when dealing with object instantiation or memory allocation. If you use records and pointers to records, stick to new/dispose. – alzaimar Jan 09 '13 at 07:27
  • @Frank No, he really means on the heap – David Heffernan Jan 09 '13 at 07:54
  • @alzaimar 1. "Wasting the stacks space"? Are you kidding me? Those records are mostly pointers, so just a few bytes. 2. When you allocate memory manually, you NEED/SHALL/ARE REQUIRED to protect the memory with try..finally block. New/Dispose is NOT protected against memory leaks (therefore your comment is wrong and misleading), whereas records allocated on stack are protected. This was the main point of my answer. 3. Memory leak may occur only if you record *contains* a reference-counted variable - which was the point of the `TCustomer` type here. This was what I tried to say. – Arnaud Bouchez Jan 09 '13 at 12:54
  • 1. A local record variable is located on the stack. A local object is located on the heap (the pointer itself is on the stack though). If the record contains short strings or arrays, more stack is wasted. So, in general, try to avoid local record variables. 2. When using New/Dispose with a record pointer you don't have to care about the size, that was my point. 3. Yep. Misunderstanding. – alzaimar Jan 09 '13 at 20:43
  • Add: In simple cases (I was ignoring this special case) It is -of course- easier to simply allocate the record as a static local variable. I was getting to general (should read the questions more thoroughly) – alzaimar Jan 09 '13 at 20:50
  • @alzaimar No, in general try to put records on the stack. Try to put as much as you can on the stack. – David Heffernan Jan 09 '13 at 22:42
2

The usual case for heap allocation is when the object must outlive the function that created it:

  1. It is being returned as a function result or via a var/out parameter, either directly or by returning some container.

  2. It's being stored in some object, struct or collection that is passed in or otherwise accessible inside the procedure (this includes being signaled/queued off to another thread).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Martin James
  • 24,453
  • 3
  • 36
  • 60
1

In cases of limited stack space you might prefer allocation from the heap.
Ref.

bummi
  • 27,123
  • 14
  • 62
  • 101