4.2. Memory allocators

There are several internal mechanisms, called memory allocators, in the malloc subsystem on AIX. Each memory allocator implements a different memory allocation policy. The allocation policy refers to a set of data structures and algorithms to represent the heap and to implement allocation, deallocation, and reallocation.

The malloc subsystem on AIX supports the following memory allocators:

  • 3.1 memory allocator

  • Default memory allocator

  • Default memory allocator with the malloc buckets extension

  • Debug memory allocator

It is also supported to implement the user-defined memory allocator on AIX (see 4.2.5, “User-defined malloc replacement” on page 178).

The programming interface to the malloc subsystem is the same regardless of the selected memory allocator. The selection of memory allocators is made on a per-process basis by setting the MALLOCTYPE environment value when a program is executed. There are several environment variables that affect the behavior of the selected memory allocator.

Table 4-1 explains how to specify the MALLOCTYPE environment variable in order to select a memory allocator (related environment variables and sections are also shown).

Table 4-1. Memory allocators and MALLOCTYPE
MALLOCTYPE=Memory allocatorRelated environment variablesDetailed explanation found in
3.13.1 memory allocator
  • MALLOCDEBUG

Section 4.2.1, “The 3.1 memory allocator” on page 171
(null)[1]Default memory allocator[2]
  • MALLOCDEBUG

  • MALLOCMULTIHEAP

Section 4.2.2, “The default memory allocator” on page 172
bucketsDefault memory allocator with the malloc buckets extension
  • MALLOCDEBUG

  • MALLOCMULTIHEAP

  • MALLOCBUCKETS

Section 4.2.3, “The default memory allocator with the malloc buckets extension” on page 173
debugDebug malloc allocator
  • MALLOCDEBUG

Section 4.2.4, “The debug malloc allocator” on page 176
user:archive_name[2]User-defined memory allocator[3]

N/A

Section 4.2.5, “User-defined malloc replacement” on page 178

[1] The default memory allocator is selected by un-setting the MALLOCTYPE environment variable.

[2] Where the archive_name specifies the library archive name that contains the user-defined malloc subsystem replacement subroutines.

[3] A user-defined memory allocator can also be specified in the program code, as explained in 4.2.5, “User-defined malloc replacement” on page 178.

By default, the default memory allocator is always selected unless the MALLOCTYPE environment variable is explicitly set. For example, to specify the default memory allocator with the malloc extension, do the following on the Korn shell command line:

$ export MALLOCTYPE=buckets

To reset to the default memory allocator, do the following:

$ export MALLOCTYPE=

Note

These memory allocators are mutually exclusive.


4.2.1. The 3.1 memory allocator

The 3.1 memory allocator is mainly provided to support applications that were originally developed on AIX Version 3. Some of those applications may depend on the behavior of the memory allocator on AIX Version 3 and may misbehave when used with the other memory allocators. Use this memory allocator only when it is absolutely required by a specific set of applications.

To select the 3.1 memory allocator, set the MALLOCTYPE environment variable before executing programs on the Korn shell prompt:

$ export MALLOCTYPE=3.1

The 3.1 memory allocator maintains the process heap as a set of 28 hash buckets, each of which points to a linked list. Hashing is a method of transforming a search key into an address for the purpose of storing and retrieving items of data. The method is designed to minimize the average search time. A bucket is one or more fields in which the result of an operation is kept. Each linked list contains blocks of a particular size. The index into the hash buckets indicates the size of the blocks in the linked list. The size of the block is calculated using the following formula:

size = 2 i + 4
					

where i identifies the bucket. This means that the blocks in the list anchored by bucket zero are 20 + 4 = 16 bytes long. Therefore, given that a prefix is 8 bytes in size, these blocks can satisfy requests for blocks between 0 and 8 bytes long.

To use the 3.1 memory allocator, keep the following points in mind:

  • The 3.1 memory allocator supports the 32-bit user process environment only. It does not support the 64-bit user process environment.

  • The 3.1 memory allocator does not support the following environment variables. If these are set, the 3.1 memory allocator simply ignores them:

    - MALLOCMULTIHEAP

    - MALLOCBUCKETS

  • The algorithm can use as much as twice the amount of memory actually requested by the application. An extra page is required for buckets larger than 4096 bytes because objects of larger page are page-aligned. Because the prefix immediately precedes the block, an entire page is required solely for the prefix.

The 3.1 memory allocator supports the MALLOCDEBUG environment variable with the following keywords:

  • log

  • verbose

  • trace

4.2.2. The default memory allocator

The default allocation policy maintains the free space in the heap as a binary tree, in which nodes are sorted vertically by length and horizontally by address. The data structure imposes no limitation on the number of block sizes supported by the tree, allowing a wide range of potential block sizes. Tree-reorganization techniques optimize access times for node location, insertion, and deletion, and also protect against fragmentation.

To select the default memory allocator, the MALLOCTYPE environment variable must be unset before executing programs. To confirm if it is unset, do following on the Korn shell prompt:

$ echo $MALLOCTYPE

$

The echo command should return a blank line, as shown in the above example.

The default memory allocator supports the following:

  • Both the 32- and 64-bit user process environment

  • The MALLOCMULTIHEAP environment variable

  • The MALLOCDEBUG environment variable with the following keywords:

    - log

    - verbose

    - arena_check

    - trace

The default memory allocator does not support the MALLOCBUCKETS environment variable. If it is set, the allocator simply ignores it.

4.2.3. The default memory allocator with the malloc buckets extension

The default memory allocator with the malloc buckets extension, or simply malloc buckets, provides an optional buckets-based extension of the default allocator. It is intended to improve malloc subsystem performance for applications that issue large numbers of small allocation requests. When malloc buckets is enabled, allocation requests that fall within a predefined range of block sizes are processed by malloc buckets. All other requests are processed in the usual manner by the default allocator.

To select malloc buckets, set the MALLOCTYPE environment variable before executing programs on the Korn shell prompt:

$ export MALLOCTYPE=buckets

Additional user configuration can be done by explicitly setting the MALLOCBUCKETS environment value (see “MALLOCBUCKETS” on page 174).

Bucket composition and sizing

A bucket consists of a block of memory that is subdivided into a predetermined number of smaller blocks of uniform size, each of which is an allocatable unit of memory. Each bucket is identified using a bucket number. The first bucket is bucket 0, the second bucket is bucket 1, the third bucket is bucket 2, and so on. The first bucket is the smallest, and each succeeding bucket is larger in size than the preceding bucket, using a formula described later in this section. A maximum of 128 buckets is available per heap.

The block size for each bucket is a multiple of a bucket-sizing factor. The bucket-sizing factor equals the block size of the first bucket. Each block in the second bucket is twice this size, each block in the third bucket is three times this size, and so on. Therefore, a given bucket’s block size is determined as follows:

block size = (bucket number + 1) * bucket sizing factor

For example, a bucket-sizing factor of 16 would result in a block size of 16 bytes for the first bucket (bucket 0), 32 bytes for the second bucket (bucket 1), 48 bytes for the third bucket (bucket 2), and so on.

The bucket-sizing factor must be a multiple of 8 for 32-bit implementations and a multiple of 16 for 64-bit implementations in order to guarantee that addresses returned from malloc subsystem functions are properly aligned for all data types.

The bucket size for a given bucket is determined as follows:

bucket size = number of blocks per bucket *
              (malloc overhead + ((bucket number + 1) * bucket sizing factor))

The preceding formula can be used to determine the actual number of bytes required for each bucket. In this formula, malloc overhead refers to the size of an internal malloc construct that is required for each block in the bucket. This internal construct is 8 bytes long for 32-bit applications and 16 bytes long for 64-bit applications. It is not part of the allocatable space available to the user, but is part of the total size of each bucket.

The default memory allocator with the malloc buckets extension supports the following:

  • Both the 32- and 64-bit user process environment

  • The MALLOCMULTIHEAP environment variable

  • The MALLOCDEBUG environment variable with the following keywords:

    - log

    - verbose

    - arena_check

    - trace

MALLOCBUCKETS

The number of blocks per bucket, number of buckets, and bucket-sizing factor are all set with the MALLOCBUCKETS environment variable. The syntax of the variable is as follows (multiple keywords can be separated by a comma):

MALLOCBUCKETS=[[ number_of_buckets:N | bucket_sizing_factor:N |
blocks_per_bucket:N | bucket_statistics:[stdout|stderr|path_name]],...]

Where:

number_of_buckets:NThis option can be used to specify the number of buckets available per heap, where N is the number of buckets. The value specified for N will apply to all available heaps. The default value for number_of_buckets is 16. The minimum value allowed is 1. The maximum value allowed is 128.
bucket_sizing_factor:NThis option can be used to specify the bucket-sizing factor, where N is the bucket-sizing factor in bytes. The value specified for bucket_sizing_factor must be a multiple of 8 for 32-bit implementations and a multiple of 16 for 64-bit implementations.
 The default value for bucket_sizing_factor is 32 for 32-bit implementations and 64 for 64-bit implementations.
blocks_per_bucket:NThis option can be used to specify the number of blocks initially contained in each bucket, where N is the number of blocks. This value is applied to all of the buckets. The value of N is also used to determine how many blocks to add when a bucket is automatically enlarged because all of its blocks have been allocated. The default value for blocks_per_bucket is 1024.
bucket_statistics:[stdout|stderr|path_name]The bucket_statistics option will cause the malloc subsystem to output a statistical summary for malloc buckets upon normal termination of each process that calls the malloc subsystem while malloc buckets is enabled. This summary shows buckets-configuration information and the number of allocation requests processed for each bucket. If multiple heaps have been enabled by way of malloc multiheap, the number of allocation requests shown for each bucket will be the sum of all allocation requests processed for that bucket for all heaps.
 The buckets statistical summary will be written to one of the following output destinations, as specified with the bucket_statistics option.
stdoutStandard output
stderrStandard error
path_nameA user-specified path name

 If a user-specified path name is provided, statistical output will be appended to the existing contents of the file (if any).
 Standard output should not be used as the output destination for a process whose output is piped as input into another process.
 The buckets_statistics option is disabled by default.

Note

  1. One additional allocation request will always be shown in the first bucket for the atexit() subroutine that prints the statistical summary.

  2. For multi-threaded processes, additional allocation requests will be shown for some of the buckets due to malloc subsystem calls issued by the Pthread library.


If the MALLOCBUCKETS environment variable is not set, then the default values shown in Table 4-2 are assumed.

Table 4-2. Default configuration values for malloc buckets
Configuration optionDefault value (32-bit)Default value (64-bit)
Number of buckets per heap1616
Bucket sizing factor32 bytes64 bytes
Allocation range1 to 512 bytes (inclusive)1 to 1024 bytes (inclusive)
Number of blocks initially contained in each bucket10241024
Bucket statistical summarydisableddisabled

4.2.4. The debug malloc allocator

Debugging applications that are mismanaging memory allocated via the malloc subsystem can be difficult and tedious. Most often, the problem is that data is written past the end of an allocated buffer. Since this has no immediate consequence, problems do not become apparent until much later when the space that was overwritten (usually belonging to another allocation) is used and no longer contains the data originally stored there.

The AIX malloc subsystem includes the debug memory allocator[2] to allow users to identify memory overwrites, over-reads, duplicate frees, and reuse of freed memory allocated by malloc(). This memory allocator is sometimes referred to as debug malloc.

[2] The debug memory allocator has been supported on AIX since Version 4.3.3

Memory problems detected by the debug memory allocator result in an abort() or a segmentation violation (SIGSEGV). In most cases, when an error is detected the application stops immediately and a core file is produced.

To select the debug memory allocator, set the MALLOCTYPE environment variable before executing programs on the Korn shell prompt:

$ export MALLOCTYPE=debug

Additional user configuration can be done by explicitly setting the MALLOCDEBUG environment value as follows:

MALLOCDEBUG=<options...>

where options is a comma-separated list of one or more predefined configuration options:

  • align:N

  • validate_ptrs

  • postfree_checking

  • allow_overreading

  • override_signal_handling

  • record_allocations

  • report_allocations

More than one option can be specified (and in any order) as long as options are comma-separated, as shown in the following example:

MALLOCDEBUG=align:0,validate_ptrs,report_allocations

Each configuration option should only be specified once when setting MALLOCDEBUG. If a configuration option is specified more than once per setting, only the final instance will apply. For a further detailed explanation about these options, see 4.3.1, “MALLOCDEBUG with the debug memory allocator” on page 182.

Note

The debug memory allocator does not support the several MALLOCDEBUG options explained in 4.3.2, “MALLOCDEBUG with memory allocators other than debug” on page 190.


The debug memory allocator is not appropriate for full-time, constant, or system-wide use. Although it is designed for minimal performance impact upon the application being debugged, it may have significant negative impact upon overall system throughput if it is used widely throughout a system. In particular, setting MALLOCTYPE=debug in the /etc/environment file (to enable debug malloc for the entire system) is unsupported, and will likely cause significant system problems, such as excessive use of paging space. The debug memory allocator should only be used to debug single applications or small groups of applications at the same time.

In addition, please note that the debug memory allocator is not appropriate for use in some debugging situations. Because the debug memory allocator places each individual memory allocation on a separate page, programs that issue many small allocation requests will see their memory usage increase dramatically. These programs may encounter new failures as memory allocation requests are denied due to a lack of memory or paging space. These failures are not necessarily errors in the program being debugged, and they are not errors in the debug memory allocator.

4.2.5. User-defined malloc replacement

If none of memory allocators provided by AIX did not satisfy the specific memory management requirement for your applications, you can implement your own memory allocator, called a user-defined memory allocator, on AIX. This functionality is referred to as a user-defined malloc replacement.

Once the user-defined memory allocator has been prepared, all the calls to the malloc subsystem from user applications, such as malloc(), free(), and so on, are transparent; no modification is necessary.

In the C and C++ program languages linkage mechanism, it is always possible to override system subroutines (external linkage symbols) by user-defined functions (internal linkage symbols). For example, the sample source code shown in Example 4-1 defines a fake version of malloc(), which simply returns the address of global character array (as long as the program requires only one memory area up to size of BUFSIZ, this fake version of malloc() is sufficient for this program). If compiled and executed, the program prints the following output:

$ cc fake_malloc.c
$ a.out
p = 1234567890
$ echo $?
0

Example 4-1. fake_malloc.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

char global_buf[BUFSIZ];

/* fake malloc() routine. */
void *malloc(size_t sz)
{
    /* return the address of global character array. */
    return((void *)global_buf);
}
int
main(int argc, char *argv[])
{
    char *p;

    if ((p = (char *)malloc(BUFSIZ)) == (char *)NULL) {
        perror("malloc() failed.
");
        exit(1);
    }
    /* write some data to the address pointed by p. */
    strcpy(p, "1234567890");
    /* print the written data. */
    printf("p = %s
", p);

    exit(0);
}

It must be clear that this overriding of external symbol names is different from the user-defined malloc replacement. If programmed appropriately, the user-defined memory allocator will be used for all the malloc subsystem calls, not only from user codes, but also from functions within shared libraries (many subroutines within shared libraries provided by AIX call the malloc subsystem internally).

To implement user-defined memory allocators, the following requirements must be satisfied:

  • A user defined memory memory allocator must provide both the 32- and 64-bit object modules. Both modules must be placed in a library archive file and the 32-bit shared object must be named mem32.o and the 64-bit shared object must be named mem64.o.

  • A user-defined memory memory allocator should be thread-safe. This is the programmer’s responsibility; there are no automatic checks to verify it.

  • A user-defined memory memory allocator must implement the functions with those names that start with double underscore characters, as shown in Table 4-3.

Table 4-3. User-defined replacement subroutines
Function name, proto-type declarationDescription
void *__malloc__(size_t)A user-defined replacement of malloc().
void __free__(void *)A user-defined replacement of free().
void *__realloc__(void *, size_t)A user-defined replacement of realloc().
void *__calloc__(size_t, size_t)A user-defined replacement of calloc().
int _mallopt_(int, int)A user-defined replacement of mallopt().
struct mallinfo _mallinfo_()A user-defined replacement of mallinfo().
void _malloc_once_()Will be called once before any other user-defined malloc entry point is called.
void _malloc_init_(void)Called by the Pthread initialization routine to initialize the user-defined memory allocator in the multi-threaded programming environment.[1]
void _malloc_prefork_lock_(void)Called by Pthread when the fork() subroutine is called.[1]
void _malloc_postfork_unlock_(void)Called by Pthread when the fork() subroutine is called.[1]

[1] These functions are mandatory in the multi-threaded environment.

  • The shared objects (mem32.o for 32-bit and mem64.o for 64-bit) must export the following symbols:

    - __malloc__

    - __free__

    - __realloc__

    - __calloc__

    - __mallinfo__

    - __mallopt__

    - __malloc_init__

    - __malloc_prefork_lock__

    - __malloc_postfork_unlock__

    The shared objects can optionally export __malloc_once__.

To select the user-defined memory allocator, set the MALLOCTYPE environment variable before executing programs on the Korn shell prompt:

$ export MALLOCTYPE=user:archive_name
					

Where the archive_name specifies the library archive name that contains the user-defined malloc subsystem replacement subroutines.

A user-defined memory allocator can also be specified in the program code by declaring the global symbol _malloc_user_defined_name, as shown in the following example:

char *_malloc_user_defined_name="archive_name";

If both the MALLOCTYPE environment variable and the global symbol are used to specify the archive_name, the name specified by MALLOCTYPE will override the one specified by the global symbol.

Note

User-defined memory allocators written in C++ are not supported, because the C++ standard library libC.a depends on the standard malloc subsystem provided by the C standard library libc.a.


4.2.6. Malloc multiheap

Historically, the malloc subsystem was designed for the non-threaded programming environment. Therefore, there was an single memory pool, or memory heap, per-process basis. After the evolution of the multi-threaded programming environment, it was realized that the single heap does not satisfy the memory allocation requests from multi-threaded applications, because a single malloc() call from a user thread can lock the entire malloc subsystem and the other user threads would be starving; thus, malloc() calls within a process would be serialized.

By providing multiple heaps, malloc multiheap efficiently supports the memory allocation requests from multi-threaded applications; thus, the malloc multiheap has a finer locking mechanism than the single heap malloc subsystem. The potential performance enhancement is particularly likely for multi-threaded C++ programs, because these may make use of the malloc subsystem whenever a constructor or destructor is called.

Beginning with Version 5.1, the malloc multiheap is enabled by default in the malloc subsystem on AIX. Therefore, it does not require any user settings, though it can be tuned using the MALLOCMULTIHEAP environment variable. To set the MALLOCMULTIHEAP environment variable, use the following syntax:

MALLOCMULTIHEAP=[[heaps:N],[considersize],...]

Where:

heaps:NThe heaps:N option can be used to change the maximum number of heaps to any value from 1 through 32, where N is the number of heaps. If n is set to a value outside the given range, the default value of 32 is used.
considersizeBy default, malloc multiheap selects the next available heap. If the considersize option is specified, malloc multiheap will use an alternate heap-selection algorithm that tries to select an available heap that has enough free space to handle the request. This may minimize the working set size of the process by reducing the number of sbrk subroutine calls. However, because of the additional processing required, the considersize heap-selection algorithm is somewhat slower than the default heap selection algorithm.

Multiple keywords are separated by a comma.

Note

The malloc multiheap is enabled internally on AIX beginning with Version 5.1 with the default value of 32. Therefore, echo $MALLOCMULTIHEAP prints a blank line by default.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset