apr-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Stefan Fuhrmann <stef...@apache.org>
Subject [PATCH] Prevent huge pool allocations for small requests.
Date Mon, 21 Dec 2015 23:42:15 GMT
Hi there,

this is basically a follow-up to r1594729.

Investigating an out-of-memory issue, I discovered
another unfortunate allocation sequence / condition
under which the allocator reuses huge nodes for
small allocations (pseudo-code):

	temp_pool = apr_pool_create();
	while (many-times)
		apr_pool_clear(temp_pool);
		long_lived_pool = apr_pool_create();		
		apr_palloc(temp_pool, 1MB);

If no small nodes (i.e. <= 80 kB) is available, all
the long-living pools will reuse the 1 MB temp. node
for its initial allocation.

The problem is that those huge nodes are kept under
index 0, so the limiter code of r1594729 will not kick
in if the apr_allocator_t.free array is empty because
max_index is 0 in that case.

Also, it would be very nice to port both patches back
to 1.4+.

-- Stefan^2.


[[[
Follow-up on r1594729:  Handle the case where the allocator
only contains large nodes (>80k or 20 pages).  We don't
want to waste them on small allocations.

However, we will not limit the allocation size for larger
requests, i.e. those may be wasteful.  They tend to either
get released quickly and or are followed by similar
allocations from the same pool - reducing the waste.
max_free_index will also limit the recycled block size.

Finally, fragmentation may become an issue with large nodes.
Hence, reusing them as they are may be the less risky option
overall.

* memory/unix/apr_pools.c
   (allocator_alloc): Only reuse large nodes for relatively
                      large allocation requests.
]]]

[[[
Index: memory/unix/apr_pools.c
===================================================================
--- memory/unix/apr_pools.c	(revision 1721274)
+++ memory/unix/apr_pools.c	(working copy)
@@ -329,9 +329,10 @@
      }

      /* If we found nothing, seek the sink (at index 0), if
-     * it is not empty.
+     * it is not empty.  However, don't be to eager to reuse
+     * a large node for a small allocation.
       */
-    else if (allocator->free[0]) {
+    else if (allocator->free[0] && (index > MAX_INDEX / 2)) {
  #if APR_HAS_THREADS
          if (allocator->mutex)
              apr_thread_mutex_lock(allocator->mutex);
]]]

Mime
View raw message