This is the mail archive of the libc-help@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Glibc malloc & using madvise


Hello,

while trying to hunt down some suspected memory leaks, I realized that
glibc implementation of malloc does not (unlike some other malloc
implementations such as jemalloc) free unused memory with
madvise(..., MADV_DONTUSE), unless the function mtrim is called.

I assume the reason for this is the fact that system calls are
relatively expensive. Nevertheless, I wrote a small patch (against glibc
2.8+20080809) that makes this behavior tunable: after setting
environment variable MALLOC_AGRESSIVE_MADVISE_=1, all memory that can be
returned to system by madvise syscall is returned as soon as possible.

This patch did quite good job for me - memory usage of some applications
(especially long-running ones with high peaks and deep valleys in memory
usage, such as xserver and konqueror) dropped by tenths of megabytes, so
there is more space for cache and less swapping in suspend/resume, and I
have not noticed any slowdown. Is there any chance of applying this (or
some similar) patch?

Greets
	Richard


diff -ur glibc-2.8+20080809/malloc/arena.c glibc-2.8+20080809-new/malloc/arena.c
--- glibc-2.8+20080809/malloc/arena.c	2007-12-12 01:11:27.000000000 +0100
+++ glibc-2.8+20080809-new/malloc/arena.c	2008-10-18 22:03:42.000000000 +0200
@@ -541,6 +541,10 @@
 		    mALLOPt(M_MMAP_THRESHOLD, atoi(&envline[16]));
 		}
 	      break;
+	    case 19:
+	      if ( memcmp (envline, "AGGRESSIVE_MADVISE_", 19) == 0)
+		mALLOPt(M_AGGRESSIVE_MADVISE, atoi(&envline[20]));
+	      break;
 	    default:
 	      break;
 	    }
@@ -559,6 +563,8 @@
 	mALLOPt(M_MMAP_THRESHOLD, atoi(s));
       if((s = getenv("MALLOC_MMAP_MAX_")))
 	mALLOPt(M_MMAP_MAX, atoi(s));
+      if((s = getenv("MALLOC_AGGRESSIVE_MADVISE_")))
+	mALLOPt(M_AGGRESSIVE_MADVISE, atoi(s));
     }
   s = getenv("MALLOC_CHECK_");
 #endif
diff -ur glibc-2.8+20080809/malloc/malloc.c glibc-2.8+20080809-new/malloc/malloc.c
--- glibc-2.8+20080809/malloc/malloc.c	2008-03-04 19:36:04.000000000 +0100
+++ glibc-2.8+20080809-new/malloc/malloc.c	2008-10-20 14:53:12.000000000 +0200
@@ -2502,6 +2502,30 @@
 #define free_perturb(p, n) memset (p, perturb_byte & 0xff, n)
 
 
+/* ------------------ Support for extensive MADV_DONTUSE -------------- */
+
+static int aggressive_madvise = 0;
+
+static void madvise_free_chunk(mchunkptr p, void *orig_start, void *orig_end) {
+  const size_t ps = mp_.pagesize;
+  const size_t psm1 = ps - 1;
+
+  /* Interior of p - the area that should be madvised */
+  char *start = (char *)(((uintptr_t) p + sizeof(struct malloc_chunk) + psm1) & ~psm1);
+  char *end = (char *)(((uintptr_t) p + chunksize(p)) & ~psm1);
+
+  /* compute ranges that already were madivsed */
+  orig_start = (void *)(((uintptr_t)orig_start - 1) & ~psm1);
+  orig_end = (void *)(((uintptr_t)orig_end + sizeof(struct malloc_chunk) + psm1) & ~psm1);
+
+  if ((char *)orig_start > start) start = orig_start;
+  if ((char *)orig_end < end) end = orig_end;
+
+  if (end > start)
+    madvise( start, end-start, MADV_DONTNEED);
+}
+
+
 /* ------------------- Support for multiple arenas -------------------- */
 #include "arena.c"
 
@@ -4629,6 +4653,9 @@
   */
 
   else if (!chunk_is_mmapped(p)) {
+    void *orig_start = p;
+    void *orig_end = (void *)p + size;
+
     nextchunk = chunk_at_offset(p, size);
 
     /* Lightweight tests: check whether the block is already the
@@ -4719,6 +4746,8 @@
       check_chunk(av, p);
     }
 
+    if (aggressive_madvise) madvise_free_chunk(p, orig_start, orig_end);
+
     /*
       If freeing a large space, consolidate possibly-surrounding
       chunks. Then, if the total unused topmost memory exceeds trim
@@ -4837,6 +4866,9 @@
         *fb = 0;
 
         do {
+          void *orig_start;
+          void *orig_end;
+
           check_inuse_chunk(av, p);
           nextp = p->fd;
 
@@ -4844,6 +4876,7 @@
           size = p->size & ~(PREV_INUSE|NON_MAIN_ARENA);
           nextchunk = chunk_at_offset(p, size);
           nextsize = chunksize(nextchunk);
+          orig_start = p; orig_end = (void *)p + size;
 
           if (!prev_inuse(p)) {
             prevsize = p->prev_size;
@@ -4882,6 +4915,8 @@
             av->top = p;
           }
 
+          if (aggressive_madvise) madvise_free_chunk(p, orig_start, orig_end);
+
         } while ( (p = nextp) != 0);
 
       }
@@ -5783,6 +5818,10 @@
   case M_PERTURB:
     perturb_byte = value;
     break;
+
+  case M_AGGRESSIVE_MADVISE:
+    aggressive_madvise = value;
+    break;
   }
   (void)mutex_unlock(&av->mutex);
   return res;
diff -ur glibc-2.8+20080809/malloc/malloc.h glibc-2.8+20080809-new/malloc/malloc.h
--- glibc-2.8+20080809/malloc/malloc.h	2007-07-19 19:05:07.000000000 +0200
+++ glibc-2.8+20080809-new/malloc/malloc.h	2008-10-18 22:00:17.000000000 +0200
@@ -127,6 +127,7 @@
 #define M_MMAP_MAX          -4
 #define M_CHECK_ACTION      -5
 #define M_PERTURB	    -6
+#define M_AGGRESSIVE_MADVISE  -7
 
 /* General SVID/XPG interface to tunable parameters. */
 extern int mallopt __MALLOC_P ((int __param, int __val));



Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]