Memory Management API¶
NGINX uses memory pools for efficient, leak-free memory management. All request-scoped allocations use the request's pool and are automatically freed when the request completes.
Pools (ngx_pool_t)¶
Creating and Destroying Pools¶
/* Create a pool with initial 4KB block */
ngx_pool_t *pool = ngx_create_pool(4096, log);
if (pool == NULL) {
return NGX_ERROR;
}
/* Use the pool... */
/* Destroy when done (frees all memory allocated from it) */
ngx_destroy_pool(pool);
Basic Allocation¶
/* Allocate aligned memory (most common) */
my_struct_t *data = ngx_palloc(pool, sizeof(my_struct_t));
/* Allocate unaligned memory (slightly faster) */
u_char *buf = ngx_pnalloc(pool, 256);
/* Allocate and zero-initialize */
my_struct_t *data = ngx_pcalloc(pool, sizeof(my_struct_t));
Complete Example: Custom Module Context¶
typedef struct {
ngx_str_t value;
ngx_flag_t enabled;
ngx_uint_t count;
} ngx_http_mymodule_ctx_t;
static ngx_int_t
ngx_http_mymodule_handler(ngx_http_request_t *r)
{
ngx_http_mymodule_ctx_t *ctx;
/* Check if context already exists */
ctx = ngx_http_get_module_ctx(r, ngx_http_mymodule_module);
if (ctx == NULL) {
/* Allocate from request pool - auto-freed when request ends */
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_mymodule_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
/* Initialize */
ctx->enabled = 1;
ctx->count = 0;
/* Store in request */
ngx_http_set_ctx(r, ctx, ngx_http_mymodule_module);
}
ctx->count++;
return NGX_DECLINED;
}
Pool Cleanup Handlers¶
Register callbacks to run when the pool is destroyed:
static void
ngx_http_mymodule_cleanup(void *data)
{
ngx_http_mymodule_ctx_t *ctx = data;
/* Close file handles, free external resources, etc. */
if (ctx->fd != NGX_INVALID_FILE) {
ngx_close_file(ctx->fd);
}
}
static ngx_int_t
ngx_http_mymodule_init(ngx_http_request_t *r)
{
ngx_pool_cleanup_t *cln;
ngx_http_mymodule_ctx_t *ctx;
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_mymodule_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
/* Register cleanup handler */
cln = ngx_pool_cleanup_add(r->pool, 0);
if (cln == NULL) {
return NGX_ERROR;
}
cln->handler = ngx_http_mymodule_cleanup;
cln->data = ctx;
return NGX_OK;
}
Arrays (ngx_array_t)¶
Growable arrays allocated from a pool.
Creating and Using Arrays¶
ngx_array_t *arr;
ngx_str_t *name;
/* Create array for 4 initial elements */
arr = ngx_array_create(r->pool, 4, sizeof(ngx_str_t));
if (arr == NULL) {
return NGX_ERROR;
}
/* Push elements */
name = ngx_array_push(arr);
if (name == NULL) {
return NGX_ERROR;
}
ngx_str_set(name, "first");
name = ngx_array_push(arr);
if (name == NULL) {
return NGX_ERROR;
}
ngx_str_set(name, "second");
/* Iterate over array */
ngx_str_t *names = arr->elts;
ngx_uint_t i;
for (i = 0; i < arr->nelts; i++) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"name[%ui]: %V", i, &names[i]);
}
Real Example: Collecting Headers¶
static ngx_int_t
ngx_http_collect_headers(ngx_http_request_t *r, ngx_array_t *headers)
{
ngx_list_part_t *part;
ngx_table_elt_t *h, *header;
ngx_uint_t i;
part = &r->headers_in.headers.part;
h = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
h = part->elts;
i = 0;
}
/* Push header into our array */
header = ngx_array_push(headers);
if (header == NULL) {
return NGX_ERROR;
}
*header = h[i];
}
return NGX_OK;
}
Lists (ngx_list_t)¶
Linked lists of fixed-size elements, ideal for headers.
ngx_list_t *list;
ngx_table_elt_t *h;
/* Create list for headers */
list = ngx_list_create(r->pool, 4, sizeof(ngx_table_elt_t));
if (list == NULL) {
return NGX_ERROR;
}
/* Add a header */
h = ngx_list_push(list);
if (h == NULL) {
return NGX_ERROR;
}
h->hash = 1;
ngx_str_set(&h->key, "X-Custom-Header");
ngx_str_set(&h->value, "custom-value");
Buffers and Chains¶
Creating a Response Buffer¶
static ngx_int_t
ngx_http_mymodule_send_response(ngx_http_request_t *r)
{
ngx_buf_t *b;
ngx_chain_t out;
ngx_int_t rc;
u_char *response = (u_char *) "Hello, World!";
size_t len = ngx_strlen(response);
/* Set response headers */
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = len;
ngx_str_set(&r->headers_out.content_type, "text/plain");
/* Send headers */
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
return rc;
}
/* Create buffer */
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
/* Copy data to buffer */
ngx_memcpy(b->pos, response, len);
b->last = b->pos + len;
b->last_buf = 1; /* This is the last buffer in the response */
b->last_in_chain = 1;
/* Create chain */
out.buf = b;
out.next = NULL;
/* Send body */
return ngx_http_output_filter(r, &out);
}
Chaining Multiple Buffers¶
static ngx_int_t
ngx_http_send_chunked_response(ngx_http_request_t *r)
{
ngx_buf_t *b1, *b2;
ngx_chain_t *out, *cl;
/* First chunk */
b1 = ngx_create_temp_buf(r->pool, 64);
if (b1 == NULL) {
return NGX_ERROR;
}
b1->last = ngx_sprintf(b1->pos, "First chunk\n");
b1->last_buf = 0;
/* Second chunk */
b2 = ngx_create_temp_buf(r->pool, 64);
if (b2 == NULL) {
return NGX_ERROR;
}
b2->last = ngx_sprintf(b2->pos, "Second chunk\n");
b2->last_buf = 1;
b2->last_in_chain = 1;
/* Link buffers into chain */
out = ngx_alloc_chain_link(r->pool);
if (out == NULL) {
return NGX_ERROR;
}
out->buf = b1;
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = b2;
cl->next = NULL;
out->next = cl;
return ngx_http_output_filter(r, out);
}
Best Practices¶
Memory Rules
- Always use pools - Never use
malloc()/free()directly - Use the right pool - Request pool for request data, connection pool for connection data
- Don't free individual allocations - Pools free everything at once
- Register cleanup handlers for external resources (files, sockets)
- Use
ngx_pcallocwhen you need zero-initialized memory