Jump to content

C Programming/stdlib.h/malloc

From Wikibooks, open books for an open world

In computing, malloc is a subroutine for performing dynamic memory allocation. malloc is part of the standard library and is declared in the stdlib.h header.

Many implementations of malloc are available, each of which performs differently depending on the computing hardware and how a program is written. Performance varies in both execution time and required memory. The pointer to memory allocated using malloc must eventually be passed to the free subroutine to deallocate the memory in order to avoid memory leaks.

Rationale

[edit | edit source]

The C programming language manages memory statically, automatically, or dynamically. Static-duration variables are allocated in main (fixed) memory and persist for the lifetime of the program; automatic-duration variables are allocated on the stack and come and go as functions are called and return. For static-duration and, before C99 (which allows variable-length automatic arrays[1]), automatic-duration variables, the size of the allocation is required to be compile-time constant. If the required size is not known until run-time (for example, if data of arbitrary size is being read from the user or from a disk file), then using fixed-size data objects is inadequate.

The lifetime of allocated memory is also a concern. Neither static- nor automatic-duration memory is adequate for all situations. Automatic-allocated data cannot persist across multiple function calls, while static data persists for the life of the program whether it is needed or not. In many situations the programmer requires greater flexibility in managing the lifetime of allocated memory.

These limitations are avoided by using dynamic memory allocation in which memory is more explicitly (but more flexibly) managed, typically, by allocating it from the heap, an area of memory structured for this purpose. In C, the library function malloc is used to allocate a block of memory on the heap. The program accesses this block of memory via a pointer that malloc returns. When the memory is no longer needed, the pointer is passed to free which deallocates the memory so that it can be used for other purposes.

Some platforms provide library calls which allow run-time dynamic allocation from the C stack rather than the heap (e.g. Unix alloca(),[2] Microsoft Windows CRTL's malloca()[3]). This memory is automatically freed when the calling function ends. The need for this is lessened by changes in the C99 standard, which added support for variable-length arrays of block scope having sizes determined at runtime.

Dynamic memory allocation in C

[edit | edit source]

The malloc function is one of the functions in standard C to allocate memory. It is just like a array. Its function prototype is:

void *malloc(size_t size);

which allocates size bytes of memory. If the allocation succeeds, a pointer to the block of memory is returned. This pointer is guaranteed to be suitably aligned to any type (including struct and such), otherwise a NULL pointer is returned.

Memory allocated via malloc is persistent: it will continue to exist, even after the program leaves the scope which called the allocation, until the program terminates or the memory is explicitly deallocated by the programmer. This is achieved by use of the free subroutine. Its prototype is

void free(void *pointer);

which releases the block of memory pointed to by pointer. pointer must have been previously returned by malloc, calloc, or realloc and pointer must not be used after it has been passed to free. In particular memory allocated via new or new[] should not be passed to free, and pointers which did not come from a malloc (e.g. stack variables) or which have already been freed must not be sent to free. It is safe to call free on a NULL pointer, which has no effect.

Usage example

[edit | edit source]

The standard method of creating an array of 10 int objects:

int array[10];

However, if one wishes to allocate a similar array dynamically, the following code could be used:

/* Allocate space for an array with ten elements of type
   int. */
int *ptr = (int *) malloc(10 * sizeof(int));
if (ptr == NULL) {
    /* Memory could not be allocated, the program should
       handle the error here as appropriate. */
} else {
    /* Allocation succeeded.  Do something.  */
    free(ptr);  /* We are done with the int objects, and
                   free the associated pointer. */
    ptr = NULL; /* The pointed-to-data  must not be used again,
                   unless re-assigned by using malloc
                   again. */
}

malloc returns a null pointer to indicate that no memory is available, or that some other error occurred which prevented memory being allocated.

Since there is a possibility that a call to malloc may fail for lack of sufficient memory, it is often convenient to define a macro that invokes malloc and exits when malloc fails. A possible macro definition is:

if(!(ptr=malloc(sizeof(int))))
{
fprintf(stderr,"Insufficient memory");   /* Prints the necessary error statement. */
exit(EXIT_FAILURE);  /* Exits <code>malloc</code> as there is insufficient memory. */
}

The macro given for this would be :

#define MALLOC(p,s)   /*Here p & s are 2 integer pointer & variable respectively. */ \
if(!((p)=malloc(s))) \
{ \
fprintf(stderr,"Insufficient memory");   /* Prints the necessary error statement. */ \
exit(EXIT_FAILURE);   /* Exits <code>malloc</code> as there is insufficient memory. */ \
}

So the above basic code can be replaced with just one line as:

MALLOC(ptr,sizeof(int));

A useful idiom with malloc is shown in this example:

int *ptr = malloc(10 * sizeof(*ptr));

That is, instead of writing a hard-wired type into the argument to malloc, one uses the sizeof operator on the content of the pointer to be allocated. This ensures that the types on the left and right of the assignment will never get out of sync when code is revised.

'C' function for creating & returning a two dimensional array of size of m*n

      int **create(int m, n)
      {
          int **p, i;
          p = (int **)malloc(m*sizeof(int*));/* this will store base order of all the row in p */
          for(i = 0; i < m; i++)
               p[i] = (int *)malloc(n*sizeof(int));/* this will create m row of n elements */
          return p;
      }

Casting and type safety

[edit | edit source]

malloc returns a void pointer (void *), which indicates that it is a pointer to a region of unknown data type. The lack of a specific pointer type returned from malloc is type-unsafe behaviour: malloc allocates based on byte count but not on type. This distinguishes it from the C++ new operator that returns a pointer whose type relies on the operand. (see C Type Safety).

One may "cast" (see type conversion) this pointer to a specific type:

int *ptr;
ptr = malloc(10 * sizeof(*ptr)); // Without a cast
ptr = (int*)malloc(10 * sizeof(int)); // With a cast

There are advantages and disadvantages to performing such a cast.

Advantages to casting

[edit | edit source]
  • Including the cast allows for compatibility with C++, which does require the cast to be made.
  • If the cast is present and the type of the left-hand-side pointer is subsequently changed, a warning will be generated to help the programmer in correcting behaviour that otherwise could become erroneous.
  • The cast allows for older versions of malloc that originally returned a char *.[4]

Disadvantages to casting

[edit | edit source]
  • Under the ANSI C standard, the cast is redundant.
  • Adding the cast may mask failure to include the header stdlib.h, in which the prototype for malloc is found.[4][5] In the absence of a prototype for malloc, the standard requires that the C compiler assume malloc returns an int. If there is no cast, a warning is issued when this integer is assigned to the pointer; however, with the cast, this warning is not produced, hiding a bug. On certain architectures and data models (such as LP64 on 64-bit systems, where long and pointers are 64-bit and int is 32-bit), this error can actually result in undefined behaviour, as the implicitly declared malloc returns a 32-bit value whereas the actually defined function returns a 64-bit value. Depending on calling conventions and memory layout, this may result in stack smashing. This issue is not present in modern compilers, as they uniformly produce warnings that an undeclared function has been used, so a warning will still appear. For example, gcc's default behaviour is to show a warning that reads "incompatible implicit declaration of built-in function" regardless of whether the cast is present or not.
[edit | edit source]

calloc

[edit | edit source]

malloc returns a block of memory that is allocated for the programmer to use, but is uninitialized. The memory is usually initialized by hand if necessary—either via the memset function, or by one or more assignment statements that dereference the pointer. An alternative is to use the calloc function, which allocates contiguous memory and then initializes it to zero. Its prototype is

void *calloc(size_t nelements, size_t elementSize);

which allocates a region of memory, initialized to 0, of size nelements × elementSize. This can be useful when allocating an array of characters to hold a string as in the example below:

char *word = calloc(200, sizeof(char));

realloc

[edit | edit source]

It is often useful to be able to shrink or enlarge a block of memory. This can be done using realloc which returns a pointer to a memory region of the specified size, which contains the same data as the old region pointed to by pointer (truncated to the minimum of the old and new sizes). If realloc is able to resize the memory region in place, it allocates new storage, copies the required data, and frees the old pointer. If this allocation fails, realloc maintains the original pointer unaltered, and returns the null pointer value. In case of expansion, the new region of memory outside the old data that is copied is uninitialized (contents are not predictable). The function prototype is

void *realloc(void *pointer, size_t size);

If pointer is NULL then realloc behaves like malloc of the given size:

void *p = malloc(42);
void *p = realloc(NULL, 42); /* equivalent */

In both C89 and C99, realloc with length 0 is a special case. The C89 standard explicitly states that the pointer given is freed, and that the return is either a null pointer or a pointer to the newly allocated space. The C99 standard says that the behavior is implementation-defined. It's possible that malloc and realloc with size 0 return different (null and non-null) pointers. Other standards, such as the Open Group's UNIX standards, make it implementation defined whether realloc(p, 0) frees p without allocating new space, or possibly frees p and returns a valid pointer to at least zero bytes of memory. Under all standards, NULL can be returned on memory allocation failure. When using realloc, one should always use a temporary variable. For example

void *p = malloc(orig_size);
/* and later... */
void *tmp = realloc(p, big_size);
if (tmp != NULL) {
   p = tmp; /* OK, assign new, larger storage to p */
} else {
   /* handle the problem somehow */
}

If instead one did

void *p = malloc(orig_size);
/* and later... */
p = realloc(p, big_size);

and if it is not possible to obtain big_size bytes of memory, then p will have value NULL and we no longer have a pointer to the memory previously allocated for p, creating a memory leak (see below).

Common errors

[edit | edit source]

The improper use of malloc and related functions can frequently be a source of bugs.

Allocation failure

[edit | edit source]

malloc is not guaranteed to succeed—if there is no memory available, or if the program has exceeded the amount of memory it is allowed to reference, malloc will return a null pointer, which should always be checked for after allocation. Many badly coded programs do not check for malloc failure. Such a program would attempt to use the null pointer returned by malloc as if it pointed to allocated memory. Most likely the program will crash; in some environments, particularly older or smaller platforms that perform no virtual memory management, zero is a valid address so the problem will not be caught.

Memory leaks

[edit | edit source]

When a call to malloc, calloc or realloc succeeds, the returned pointer to the allocated memory should eventually be passed to the free function. This releases the allocated memory, allowing it to be reused to satisfy other memory allocation requests. If this is not done, the allocated memory will not be released until the process exits (and in some environments, not even then)—in other words, a memory leak will occur. Typically, memory leaks are caused by losing track of pointers, for example not using a temporary pointer for the return value of realloc, which may lead to the original pointer being overwritten with a null pointer, for example:

void *ptr;
size_t size = BUFSIZ;

ptr = malloc(size);

/* some further execution happens here... */

/* now the buffer size needs to be doubled */
if (size > SIZE_MAX / 2) {
  /* handle overflow error */
  /* . probably appropriate to use free( ptr ) here . */
  return (1);
}
size *= 2;
ptr = realloc(ptr, size);
if (ptr == NULL) {
  /* the realloc failed (it returned a null pointer), but
     the original address in ptr has been lost so the
     memory cannot be freed and a leak has occurred */
  /* ... */
  return 1;
}
/* ... */

Use after free

[edit | edit source]

After a pointer has been passed to free, it becomes a dangling pointer: it references a region of memory with undefined content, which may not be available for use. The pointer's value cannot be accessed. For example:

int *ptr = malloc(sizeof (int));
free(ptr);
*ptr = 7; /* Undefined behavior */

Code like this has undefined behavior: its effect may vary. Actually even trying to read the value of a freed pointer can result in undefined behaviour (here).

Commonly, the system may have reused freed memory for other purposes. Therefore, writing through a pointer to a deallocated region of memory may result in overwriting another piece of data somewhere else in the program. Depending on what data is overwritten, this may result in data corruption or cause the program to crash at a later time. A particularly bad example of this problem is if the same pointer is passed to free twice, known as a double free. 'Use after free' and 'Double free' bugs can lead to security vulnerabilities.[6] To avoid this, some programmers set pointers to NULL after passing them to free:[7]

free(ptr);
ptr = NULL; /*is safe (throws away the pointer's location).*/

However, this will not protect other aliases to the same pointer from being misused.

Freeing unallocated memory

[edit | edit source]

Another problem is when free is passed an address that was not allocated by malloc, realloc or calloc. This can be caused when a pointer to a literal string or the name of a declared array is passed to free, for example:

char *msg = "Default message";
int tbl[100];

Passing either of the above pointers to free will result in undefined behaviour.

A common error is to free the memory, then use it:

char *ch_ptr = malloc(20);
for (i = 0; i < 19; i++) ch_ptr[i] = 'A';
i[19] = '\0';
free(ch_ptr);
printf("%s\n", ch_ptr);

This is called “used after freed”. This will run on many systems. This happens when free() does not change the contents of the memory being freed. The standard for C does not guarantee this behavior. Thus, it is certain it will fail on some systems.

When a function returns a pointer to allocated memory, the usual practice is to put the pointer returned into a variable, use the memory, then free it using the pointer:

char *ch_ptr = malloc(20);
for (i = 0; i < 19; i++) ch_ptr[i] = 'A';
i[19] = '\0';
printf("%s\n", ch_ptr);
free(ch_ptr);

Freeing memory twice

[edit | edit source]

In some programs, memory blocks are freed twice. This is because of confusion that which function is responsible for memory deallocation. Example:

void main(){
      int *p;
      p = (int *)malloc(10 * sizeof(int));
      f(p);
      free(p);
}
void f(int *g){
      printf("%d", g);
      free(g);
}

This is called "double free" or "multiple free". This code will run on some systems, but may break on the second free.

Implementations

[edit | edit source]

The implementation of memory management depends greatly upon operating system and architecture. Some operating systems supply an allocator for malloc, while others supply functions to control certain regions of data. The same dynamic memory allocator is often used to implement both malloc and the operator new in C++ [citation needed]. Hence, it is referred to below as the allocator rather than malloc.

Heap-based

[edit | edit source]

Implementation of the allocator on IA-32 architectures is commonly done using the heap, or data segment. The allocator will usually expand and contract the heap to fulfill allocation requests.

The heap method suffers from a few inherent flaws, stemming entirely from fragmentation. Like any method of memory allocation, the heap will become fragmented; that is, there will be sections of used and unused memory in the allocated space on the heap. A good allocator will attempt to find an unused area of already allocated memory to use before resorting to expanding the heap. The major problem with this method is that the heap has only two significant attributes: base, or the beginning of the heap in virtual memory space; and length, or its size. The heap requires enough system memory to fill its entire length, and its base can never change. Thus, any large areas of unused memory are wasted. The heap can get "stuck" in this position if a small used segment exists at the end of the heap, which could waste any magnitude of address space, from a few megabytes to a few hundred.

dlmalloc and its derivatives

[edit | edit source]

Doug Lea is the author of a memory allocator called dlmalloc ("Doug Lea's Malloc") whose source code describes itself as:

"This is not the fastest, most space-conserving, most portable, or

most tunable malloc ever written. However it is among the fastest while also being among the most space-conserving, portable and tunable. Consistent balance across these factors results in a good general-purpose allocator for malloc-intensive programs."

The first implementation of dlmalloc was created in 1987.[8] It is written in C and is highly portable, being known to work well on all major operating systems and processor architectures and on systems ranging from medium/small embedded right up to supercomputers[citation needed]. Due to its longevity and open source nature, dlmalloc is widely used for teaching purposes and as a foundation for other allocators, the best known of which is ptmalloc2/ptmalloc3. Since the v2.3 release, the GNU C library (glibc) uses a modified ptmalloc2, which itself is based on dlmalloc v2.7.0.[9]

Another, lesser known dlmalloc derivative is nedmalloc which is based on dlmalloc v2.8.4 and is essentially dlmalloc wrapped by a per-thread lookaside cache to improve execution concurrency.

Memory on the heap is allocated as "chunks", an 8-byte aligned data structure which contains a header and usable memory. Allocated memory contains an 8 or 16 byte overhead for the size of the chunk and usage flags. Unallocated chunks also store pointers to other free chunks in the usable space area, making the minimum chunk size 24-bytes.[9]

Unallocated memory is grouped into "bins" of similar sizes, implemented by using a double-linked list of chunks (with pointers stored in the unallocated space inside the chunk).[9]

For requests below 256 bytes (a "smallbin" request), a simple two power best fit allocator is used. A CPU special op (on GCC this is __builtin_clz()) is used to very quickly find the first set bit and a block is returned for the bin corresponding to that top bit position. If there are no free blocks in that bin, a block from the next highest bin is split in two.

For requests of 256 bytes or above but below the mmap threshold, i.e. what dlmalloc calls a "largebin" request, recent versions of dlmalloc use an in-place bitwise trie algorithm. This traverses a binary tree on the basis of each bit state after the first set bit which modern out-of-order CPUs can do very efficiently and which is mostly invariant to the number of allocated blocks. A big advantage of this algorithm is that if it fails to find the size requested, it returns the block with the next biggest size which makes it very easy to split that block into the size required.

For requests above the mmap threshold, the memory is always allocated using the mmap system call. The threshold is 256 KB (1 MB on ptmalloc2) by default, but can be changed by calling the mallopt function.[10] The mmap method averts problems with huge buffers trapping a small allocation at the end after their expiration, but always allocates an entire page of memory, which on many architectures are 4096 bytes in size.[11]

If additional memory below the threshold but less than the available free space needs to be allocated, dlmalloc may use the brk() call to the Linux kernel to increase the size of the heap. Increasing the size of the heap increases the size of the top-most chunk (wilderness chunk), which is always unallocated, and is treated specially by malloc.[9]

dlmalloc has a fairly weak free space segment coalescer algorithm, mainly because free space coalescing tends to be extremely slow due to causing TLB cache exhaustion. It is called every (by default) 4096 free() operations and it works by iterating each of the segments previously requested from the system which were not contiguously returned by the system. It tries to identify large ranges of memory which contain no allocated blocks and breaking its segment into two with the free memory being returned to the system. This algorithm works well if dlmalloc is the sole user of the VM system, however if dlmalloc is used simultaneously with another allocator then dlmalloc's free space coalescer can fail to correctly identify opportunities for free memory release.

glibc implementation of ptmalloc2 differs from dlmalloc in this case as it generally requests extra memory from the kernel using mmap to allocate 1Mb aligned chunks, which its source code refers to as arenas. ptmalloc2 tries to ensure separate arenas per execution thread, thus permitting concurrency within the memory allocator.

ptmalloc3 improves significantly on ptmalloc2 by making the smallbins of dlmalloc (described above) per-thread. This allows ptmalloc3 to offer lock free concurrency for smaller blocks while still allowing separate arenas for largebin allocations. Allocations beyond the mmap threshold still route exclusively through mmap().

nedmalloc is similar to ptmalloc2 in its support of multiple per-thread arenas, but it also adds a separate per-thread lookaside cache for smaller sized blocks which avoids processor serialisation for typical C++ usage patterns much as ptmalloc3 does. nedmalloc, like Hoard referred to below, is able to patch Microsoft Windows binaries to replace the system allocator with itself within a given process. The most recent versions of nedmalloc implement a user mode page allocator which replaces mmap(), thus allowing memory pages to be held in a lookaside cache and therefore greatly improving the speed of large allocations.

All of dlmalloc, ptmalloc2, ptmalloc3 and nedmalloc are licensed under an open source licence and are therefore available for student study.

FreeBSD's and NetBSD's jemalloc

[edit | edit source]

Since FreeBSD 7.0 and NetBSD 5.0, the old malloc implementation (phkmalloc) was replaced by jemalloc, written by Jason Evans. The main reason for this was a lack of scalability of phkmalloc in terms of multithreading. In order to avoid lock contention, jemalloc uses separate "arenas" for each CPU. Experiments measuring number of allocations per second in multithreading application have shown that this makes it scale linearly with the number of threads, while for both phkmalloc and dlmalloc performance was inversely proportional to the number of threads.[12]

jemalloc is used as the default allocator, in Windows and Linux, of Firefox 3 beta4pre and later instead of the one provided by the operating system, except for Mac OS X. This improves performance and lowers memory consumption, due to less fragmentation.

OpenBSD's malloc

[edit | edit source]

OpenBSD's implementation of the malloc function makes use of mmap. For requests greater in size than one page, the entire allocation is retrieved using mmap; smaller sizes are assigned from memory pools maintained by malloc within a number of "bucket pages," also allocated with mmap. On a call to free, memory is released and unmapped from the process address space using munmap. This system is designed to improve security by taking advantage of the address space layout randomization and gap page features implemented as part of OpenBSD's mmap system call, and to detect use-after-free bugs—as a large memory allocation is completely unmapped after it is freed, further use causes a segmentation fault and termination of the program.

Hoard's malloc

[edit | edit source]

The Hoard memory allocator is an allocator whose goal is scalable memory allocation performance. Like OpenBSD's allocator, Hoard uses mmap exclusively, but manages memory in chunks of 64 kilobytes called superblocks. Hoard's heap is logically divided into a single global heap and a number of per-processor heaps. In addition, there is a thread-local cache that can hold a limited number of superblocks. By allocating only from superblocks on the local per-thread or per-processor heap, and moving mostly-empty superblocks to the global heap so they can be reused by other processors, Hoard keeps fragmentation low while achieving near linear scalability with the number of threads.[13]

Thread-caching malloc tcmalloc

[edit | edit source]

Every thread has local storage for small allocations. For large allocations mmap or sbrk can be used. Tcmalloc has garbage-collection for local storage of dead threads. The TCmalloc is considered to be more than twice as fast as glibc's ptmalloc for multithreaded programs.[14][15]

In-kernel

[edit | edit source]

Operating system kernels need to allocate memory just as application programs do. The implementation of malloc within a kernel often differs significantly from the implementations used by C libraries, however. For example, memory buffers might need to conform to special restrictions imposed by DMA, or the memory allocation function might be called from interrupt context.[16] This necessitates a malloc implementation tightly integrated with the virtual memory subsystem of the operating system kernel.

In Linux and Unix kmalloc and kfree provide the functionality of malloc and free in the kernel. In Windows drivers, ExAllocatePoolWithTag, ExAllocatePoolWithQuotaTag and ExFreePoolWithTag provide the malloc/free semantic functionality in kernel mode.

Allocation size limits

[edit | edit source]

The largest possible memory block malloc can allocate depends on the host system, particularly the size of physical memory and the operating system implementation. Theoretically, the largest number should be the maximum value that can be held in a size_t type, which is an implementation-dependent unsigned integer representing the size of an area of memory. The maximum value is 2CHAR_BIT*sizeof(size_t) − 1, or the constant SIZE_MAX in the C99 standard.

References

[edit | edit source]
  1. gcc manual on gnu.org accessed at December 14, 2008
  2. "alloca". Man.freebsd.org. 2006-09-05. Retrieved 2011-09-18.
  3. malloca() page on MSDN Visual C++ Developer Center. Accessed on 12th March 2009
  4. a b FAQ > Explanations of... > Casting malloc on Cprogramming.com accessed at March 9, 2007
  5. comp.lang.c FAQ list · Question 7.7b on C-FAQ accessed at March 9, 2007
  6. "CWE - CWE-415: Double Free (2.1)". Cwe.mitre.org. Retrieved 2011-09-18.
  7. The Open Group Base Specifications Issue 6 on The Open Group accessed at March 9, 2007
  8. "A Memory Allocator". Gee.cs.oswego.edu. Retrieved 2011-09-18.
  9. a b c d Kaempf, Michel (2001). "Vudo malloc tricks". Phrack (57): 8. Retrieved 2009-04-29. {{cite journal}}: Cite has empty unknown parameters: |coauthors= and |month= (help)
  10. "Malloc Tunable Parameters". GNU. Retrieved 2009-05-02.
  11. Sanderson, Bruce (2004-12-12). "RAM, Virtual Memory, Pagefile and all that stuff". Microsoft Help and Support.
  12. http://people.freebsd.org/~jasone/jemalloc/bsdcan2006/jemalloc.pdf
  13. http://www.cs.umass.edu/~emery/pubs/berger-asplos2000.pdf
  14. http://goog-perftools.sourceforge.net/doc/tcmalloc.html
  15. Mark Callaghan (2009-01-18). "High Availability MySQL: Double sysbench throughput with TCMalloc". Mysqlha.blogspot.com. Retrieved 2011-09-18.
  16. "kmalloc()/kfree() include/linux/slab.h". People.netfilter.org. Retrieved 2011-09-18.
[edit | edit source]