diff mbox

[1/5] Use MADV_DONTNEED for freeing in garbage collector

Message ID 1318190141-1220-2-git-send-email-andi@firstfloor.org
State New
Headers show

Commit Message

Andi Kleen Oct. 9, 2011, 7:55 p.m. UTC
From: Andi Kleen <ak@linux.intel.com>

Use the Linux MADV_DONTNEED call to unmap free pages in the garbage
collector.Then keep the unmapped pages in the free list. This avoid
excessive memory fragmentation on large LTO bulds, which can lead
to gcc bumping into the Linux vm_max_map limit per process.

Based on a idea from Jakub.

gcc/:

2011-10-08   Andi Kleen <ak@linux.intel.com>

	PR other/50636
	* config.in, configure: Regenerate.
	* configure.ac (madvise): Add to AC_CHECK_FUNCS.
	* ggc-page.c (USING_MADVISE): Add.
	(page_entry): Add unmapped field.
	(alloc_page): Check for unmapped pages.
	(release_pages): Add USING_MADVISE branch.
---
 gcc/config.in    |    6 ++++++
 gcc/configure    |    2 +-
 gcc/configure.ac |    2 +-
 gcc/ggc-page.c   |   48 +++++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 55 insertions(+), 3 deletions(-)

Comments

Richard Biener Oct. 10, 2011, 10:25 a.m. UTC | #1
On Sun, Oct 9, 2011 at 9:55 PM, Andi Kleen <andi@firstfloor.org> wrote:
> From: Andi Kleen <ak@linux.intel.com>
>
> Use the Linux MADV_DONTNEED call to unmap free pages in the garbage
> collector.Then keep the unmapped pages in the free list. This avoid
> excessive memory fragmentation on large LTO bulds, which can lead
> to gcc bumping into the Linux vm_max_map limit per process.
>
> Based on a idea from Jakub.

Shouldn't we prefer still "mapped" pages when allocating?  Thus, keep
the freepages list "sorted"?

With the new params to call release_pages less, how does this
interact with using MADV_DONTNEED?  The only reason to delay
MADV_DONTNEED is to avoid splitting huge-pages?  Which would
mean that we should rather be better at controlling where we allocate
from from the free-list?

Richard.

> gcc/:
>
> 2011-10-08   Andi Kleen <ak@linux.intel.com>
>
>        PR other/50636
>        * config.in, configure: Regenerate.
>        * configure.ac (madvise): Add to AC_CHECK_FUNCS.
>        * ggc-page.c (USING_MADVISE): Add.
>        (page_entry): Add unmapped field.
>        (alloc_page): Check for unmapped pages.
>        (release_pages): Add USING_MADVISE branch.
> ---
>  gcc/config.in    |    6 ++++++
>  gcc/configure    |    2 +-
>  gcc/configure.ac |    2 +-
>  gcc/ggc-page.c   |   48 +++++++++++++++++++++++++++++++++++++++++++++++-
>  4 files changed, 55 insertions(+), 3 deletions(-)
>
> diff --git a/gcc/config.in b/gcc/config.in
> index f2847d8..e8148b6 100644
> --- a/gcc/config.in
> +++ b/gcc/config.in
> @@ -1276,6 +1276,12 @@
>  #endif
>
>
> +/* Define to 1 if you have the `madvise' function. */
> +#ifndef USED_FOR_TARGET
> +#undef HAVE_MADVISE
> +#endif
> +
> +
>  /* Define to 1 if you have the <malloc.h> header file. */
>  #ifndef USED_FOR_TARGET
>  #undef HAVE_MALLOC_H
> diff --git a/gcc/configure b/gcc/configure
> index cb55dda..4a54adf 100755
> --- a/gcc/configure
> +++ b/gcc/configure
> @@ -9001,7 +9001,7 @@ fi
>  for ac_func in times clock kill getrlimit setrlimit atoll atoq \
>        sysconf strsignal getrusage nl_langinfo \
>        gettimeofday mbstowcs wcswidth mmap setlocale \
> -       clearerr_unlocked feof_unlocked   ferror_unlocked fflush_unlocked fgetc_unlocked fgets_unlocked   fileno_unlocked fprintf_unlocked fputc_unlocked fputs_unlocked   fread_unlocked fwrite_unlocked getchar_unlocked getc_unlocked   putchar_unlocked putc_unlocked
> +       clearerr_unlocked feof_unlocked   ferror_unlocked fflush_unlocked fgetc_unlocked fgets_unlocked   fileno_unlocked fprintf_unlocked fputc_unlocked fputs_unlocked   fread_unlocked fwrite_unlocked getchar_unlocked getc_unlocked   putchar_unlocked putc_unlocked madvise
>  do :
>   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
>  ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
> diff --git a/gcc/configure.ac b/gcc/configure.ac
> index a7b94e6..357902e 100644
> --- a/gcc/configure.ac
> +++ b/gcc/configure.ac
> @@ -1027,7 +1027,7 @@ define(gcc_UNLOCKED_FUNCS, clearerr_unlocked feof_unlocked dnl
>  AC_CHECK_FUNCS(times clock kill getrlimit setrlimit atoll atoq \
>        sysconf strsignal getrusage nl_langinfo \
>        gettimeofday mbstowcs wcswidth mmap setlocale \
> -       gcc_UNLOCKED_FUNCS)
> +       gcc_UNLOCKED_FUNCS madvise)
>
>  if test x$ac_cv_func_mbstowcs = xyes; then
>   AC_CACHE_CHECK(whether mbstowcs works, gcc_cv_func_mbstowcs_works,
> diff --git a/gcc/ggc-page.c b/gcc/ggc-page.c
> index 624f029..b0b3b3f 100644
> --- a/gcc/ggc-page.c
> +++ b/gcc/ggc-page.c
> @@ -50,6 +50,10 @@ along with GCC; see the file COPYING3.  If not see
>  #define USING_MALLOC_PAGE_GROUPS
>  #endif
>
> +#if defined(HAVE_MADVISE) && defined(MADV_DONTNEED)
> +# define USING_MADVISE
> +#endif
> +
>  /* Strategy:
>
>    This garbage-collecting allocator allocates objects on one of a set
> @@ -277,6 +281,9 @@ typedef struct page_entry
>   /* The lg of size of objects allocated from this page.  */
>   unsigned char order;
>
> +  /* Unmapped page? */
> +  bool unmapped;
> +
>   /* A bit vector indicating whether or not objects are in use.  The
>      Nth bit is one if the Nth object on this page is allocated.  This
>      array is dynamically sized.  */
> @@ -740,6 +747,10 @@ alloc_page (unsigned order)
>
>   if (p != NULL)
>     {
> +      if (p->unmapped)
> +        G.bytes_mapped += p->bytes;
> +      p->unmapped = false;
> +
>       /* Recycle the allocated memory from this page ...  */
>       *pp = p->next;
>       page = p->page;
> @@ -956,7 +967,42 @@ free_page (page_entry *entry)
>  static void
>  release_pages (void)
>  {
> -#ifdef USING_MMAP
> +#ifdef USING_MADVISE
> +  page_entry *p, *start_p;
> +  char *start;
> +  size_t len;
> +
> +  for (p = G.free_pages; p; )
> +    {
> +      if (p->unmapped)
> +        {
> +          p = p->next;
> +          continue;
> +        }
> +      start = p->page;
> +      len = p->bytes;
> +      start_p = p;
> +      p = p->next;
> +      while (p && p->page == start + len)
> +        {
> +          len += p->bytes;
> +          p = p->next;
> +        }
> +      /* Give the page back to the kernel, but don't free the mapping.
> +         This avoids fragmentation in the virtual memory map of the
> +        process. Next time we can reuse it by just touching it. */
> +      madvise (start, len, MADV_DONTNEED);
> +      /* Don't count those pages as mapped to not touch the garbage collector
> +         unnecessarily. */
> +      G.bytes_mapped -= len;
> +      while (start_p != p)
> +        {
> +          start_p->unmapped = true;
> +          start_p = start_p->next;
> +        }
> +    }
> +#endif
> +#if defined(USING_MMAP) && !defined(USING_MADVISE)
>   page_entry *p, *next;
>   char *start;
>   size_t len;
> --
> 1.7.5.4
>
>
Jakub Jelinek Oct. 10, 2011, 10:45 a.m. UTC | #2
On Mon, Oct 10, 2011 at 12:25:15PM +0200, Richard Guenther wrote:
> On Sun, Oct 9, 2011 at 9:55 PM, Andi Kleen <andi@firstfloor.org> wrote:
> > From: Andi Kleen <ak@linux.intel.com>
> >
> > Use the Linux MADV_DONTNEED call to unmap free pages in the garbage
> > collector.Then keep the unmapped pages in the free list. This avoid
> > excessive memory fragmentation on large LTO bulds, which can lead
> > to gcc bumping into the Linux vm_max_map limit per process.
> >
> > Based on a idea from Jakub.
> 
> Shouldn't we prefer still "mapped" pages when allocating?  Thus, keep
> the freepages list "sorted"?

I don't see why.  MADV_DONTNEED isn't perfect, what it does (on Linux)
is that it zaps the whole page range, which essentially brings it into
the exact same state as immediately after mmap.  Any touch of the
pages will result in a zeroed page being inserted into the page tables.

4 years ago there was a MADV_FREE proposal which behaved much better
(page was removed from page tables only when the kernel actually needed
them for something else, if the page wasn't needed and has been accessed
again by the application, it would still contain the old content (which
the app couldn't rely on, it could as well be cleared), but it would be much
cheaper in that case.  With MADV_FREE it would be actually preferrable
to pick the MADV_FREEd pages over picking up freshly munmapped but not yet
touched pages.

> With the new params to call release_pages less, how does this
> interact with using MADV_DONTNEED?  The only reason to delay
> MADV_DONTNEED is to avoid splitting huge-pages?  Which would

Not just that.  MADV_DONTNEED needs to flush the dirty pages from the page
tables and when they are touched again, they need to be cleared (or
pre-cleared pages inserted).  So, while MADV_DONTNEED is less expensive than
munmap + mmap, it is still not free.

> > 2011-10-08   Andi Kleen <ak@linux.intel.com>

Two space in between name and <.
> >
> >        PR other/50636
> >        * config.in, configure: Regenerate.

Please write each file on a separate line, and better below
* configure.ac line because of which they have been regenerated.

> >
> > +  /* Unmapped page? */
> > +  bool unmapped;
> > +

Not sure if unmapped is the best name of the flag here, because
it hasn't been unmapped, it just has been madvised.  Under unmap
most people would imagine munmap I'd say.

	Jakub
Richard Biener Oct. 10, 2011, 11:11 a.m. UTC | #3
On Mon, Oct 10, 2011 at 12:45 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Mon, Oct 10, 2011 at 12:25:15PM +0200, Richard Guenther wrote:
>> On Sun, Oct 9, 2011 at 9:55 PM, Andi Kleen <andi@firstfloor.org> wrote:
>> > From: Andi Kleen <ak@linux.intel.com>
>> >
>> > Use the Linux MADV_DONTNEED call to unmap free pages in the garbage
>> > collector.Then keep the unmapped pages in the free list. This avoid
>> > excessive memory fragmentation on large LTO bulds, which can lead
>> > to gcc bumping into the Linux vm_max_map limit per process.
>> >
>> > Based on a idea from Jakub.
>>
>> Shouldn't we prefer still "mapped" pages when allocating?  Thus, keep
>> the freepages list "sorted"?
>
> I don't see why.  MADV_DONTNEED isn't perfect, what it does (on Linux)
> is that it zaps the whole page range, which essentially brings it into
> the exact same state as immediately after mmap.  Any touch of the
> pages will result in a zeroed page being inserted into the page tables.

Which means we save the zeroing when allocating non-MADV_DONTNEEDed
pages first.  And will be eventually able to unmap zapped pages.

> 4 years ago there was a MADV_FREE proposal which behaved much better
> (page was removed from page tables only when the kernel actually needed
> them for something else, if the page wasn't needed and has been accessed
> again by the application, it would still contain the old content (which
> the app couldn't rely on, it could as well be cleared), but it would be much
> cheaper in that case.  With MADV_FREE it would be actually preferrable
> to pick the MADV_FREEd pages over picking up freshly munmapped but not yet
> touched pages.
>
>> With the new params to call release_pages less, how does this
>> interact with using MADV_DONTNEED?  The only reason to delay
>> MADV_DONTNEED is to avoid splitting huge-pages?  Which would
>
> Not just that.  MADV_DONTNEED needs to flush the dirty pages from the page
> tables and when they are touched again, they need to be cleared (or
> pre-cleared pages inserted).  So, while MADV_DONTNEED is less expensive than
> munmap + mmap, it is still not free.

But it's free at madvise time.  munmap is "synchronous" at least (well,
when file-backed).

>> > 2011-10-08   Andi Kleen <ak@linux.intel.com>
>
> Two space in between name and <.
>> >
>> >        PR other/50636
>> >        * config.in, configure: Regenerate.
>
> Please write each file on a separate line, and better below
> * configure.ac line because of which they have been regenerated.
>
>> >
>> > +  /* Unmapped page? */
>> > +  bool unmapped;
>> > +
>
> Not sure if unmapped is the best name of the flag here, because
> it hasn't been unmapped, it just has been madvised.  Under unmap
> most people would imagine munmap I'd say.
>
>        Jakub
>
Jakub Jelinek Oct. 10, 2011, 11:47 a.m. UTC | #4
On Mon, Oct 10, 2011 at 01:11:13PM +0200, Richard Guenther wrote:
> > I don't see why.  MADV_DONTNEED isn't perfect, what it does (on Linux)
> > is that it zaps the whole page range, which essentially brings it into
> > the exact same state as immediately after mmap.  Any touch of the
> > pages will result in a zeroed page being inserted into the page tables.
> 
> Which means we save the zeroing when allocating non-MADV_DONTNEEDed
> pages first.  And will be eventually able to unmap zapped pages.

Well, there are 3 kind of pages in G.free_pages list after the patch.
1) pages added by alloc_page (GGC_QUIRK_SIZE - 1 pages each time, for p->bytes ==  G.pagesize only)
2) pages on which free_page has been called during last ggc_collect's sweep_pages
3) MADV_DONTNEED hinted pages from release_pages

Pages of 1) and 3) category have the same cost, pages of 2) category are
cheaper to access.  Pages of the 3) category are guaranteed to be at the
end of list (simply because free_pages marks all pages in the G.free_pages
list).  So this patch doesn't make things worse than it was before in this
regard, though maybe we should munmap p->bytes != G.pagesize pages right
away in release_pages instead of MADV_DONTNEED marking them.  They are
rarely to be reused.  For p->bytes == G.pagesize pages (the vast majority)
on the other side 1) shouldn't be really added until there are no 2) and 3)
category pages.

> > Not just that.  MADV_DONTNEED needs to flush the dirty pages from the page
> > tables and when they are touched again, they need to be cleared (or
> > pre-cleared pages inserted).  So, while MADV_DONTNEED is less expensive than
> > munmap + mmap, it is still not free.
> 
> But it's free at madvise time.  munmap is "synchronous" at least (well,
> when file-backed).

The zapping means some TLB flush and page table clearly, so not free even at
madvise time.  And here we are talking about anon memory anyway, so not
file-backed.

	Jakub
Andi Kleen Oct. 10, 2011, 2:04 p.m. UTC | #5
> Shouldn't we prefer still "mapped" pages when allocating?  Thus, keep
> the freepages list "sorted"?

Possibly. I can look at it in a followup if you want. 
I would prefer to not complicate this patch too much.

> 
> With the new params to call release_pages less, how does this
> interact with using MADV_DONTNEED?  The only reason to delay
> MADV_DONTNEED is to avoid splitting huge-pages?  Which would
> mean that we should rather be better at controlling where we allocate
> from from the free-list?

I first had a patch that tried to cluster inside the freelist
with multiple passes (and only free aligned quire clusters first), but it 
ran into various problems, so I chose this simpler approach.

With MADV_DONTNEED the param is not really needed I think,
I mainly added the param for the benefit of hosts that don't 
have MADV_DONTNEED to let them not suffer from fragmentation too much.
It would be possible to set the thresholds all to 0 if MADV_DONTNEED
is available.

-Andi
Richard Biener Oct. 10, 2011, 2:55 p.m. UTC | #6
On Mon, Oct 10, 2011 at 4:04 PM, Andi Kleen <andi@firstfloor.org> wrote:
>> Shouldn't we prefer still "mapped" pages when allocating?  Thus, keep
>> the freepages list "sorted"?
>
> Possibly. I can look at it in a followup if you want.
> I would prefer to not complicate this patch too much.
>
>>
>> With the new params to call release_pages less, how does this
>> interact with using MADV_DONTNEED?  The only reason to delay
>> MADV_DONTNEED is to avoid splitting huge-pages?  Which would
>> mean that we should rather be better at controlling where we allocate
>> from from the free-list?
>
> I first had a patch that tried to cluster inside the freelist
> with multiple passes (and only free aligned quire clusters first), but it
> ran into various problems, so I chose this simpler approach.
>
> With MADV_DONTNEED the param is not really needed I think,
> I mainly added the param for the benefit of hosts that don't
> have MADV_DONTNEED to let them not suffer from fragmentation too much.
> It would be possible to set the thresholds all to 0 if MADV_DONTNEED
> is available.

So can we move the param patch back as a possible followup?

Thanks,
Richard.

> -Andi
>
Andi Kleen Oct. 10, 2011, 5:56 p.m. UTC | #7
> So can we move the param patch back as a possible followup?

I can drop it, however it will still mean fragmentation on any
non Linux (or rather non MADV_DONTNEED) hosts.

-Andi
Andi Kleen Oct. 16, 2011, 5:30 a.m. UTC | #8
Andi Kleen <andi@firstfloor.org> writes:

> From: Andi Kleen <ak@linux.intel.com>
>
> Use the Linux MADV_DONTNEED call to unmap free pages in the garbage
> collector.Then keep the unmapped pages in the free list. This avoid
> excessive memory fragmentation on large LTO bulds, which can lead
> to gcc bumping into the Linux vm_max_map limit per process.

Could I have a decision on this patch please? The problem in PR50636
is still there and this is the minimum fix to fix it on Linux
as far as I know.

If this patch is not the right way to go I would
appreciate some guidance on an alternative (but low cost)
implementation. Note I don't have capacity for any overly
complicated solutions.

Thanks,

-Andi
Richard Biener Oct. 16, 2011, 10:38 a.m. UTC | #9
On Sun, Oct 16, 2011 at 7:30 AM, Andi Kleen <andi@firstfloor.org> wrote:
> Andi Kleen <andi@firstfloor.org> writes:
>
>> From: Andi Kleen <ak@linux.intel.com>
>>
>> Use the Linux MADV_DONTNEED call to unmap free pages in the garbage
>> collector.Then keep the unmapped pages in the free list. This avoid
>> excessive memory fragmentation on large LTO bulds, which can lead
>> to gcc bumping into the Linux vm_max_map limit per process.
>
> Could I have a decision on this patch please? The problem in PR50636
> is still there and this is the minimum fix to fix it on Linux
> as far as I know.
>
> If this patch is not the right way to go I would
> appreciate some guidance on an alternative (but low cost)
> implementation. Note I don't have capacity for any overly
> complicated solutions.

The patch looks generally ok, but you are never giving back pages to the
system, and as we have other memory allocations that do not use the
ggc pools you drain virtual memory on 32bit hosts.  Is any other patch
in this series compensating for it?  If not I'd say we should munmap the
pages when a full mapped range (2MB) is free.  Can you rename
'unmapped' to 'discarded' please?  That would be less confusing.

Thanks,
Richard.

> Thanks,
>
> -Andi
>
> --
> ak@linux.intel.com -- Speaking for myself only
>
Andi Kleen Oct. 16, 2011, 6:33 p.m. UTC | #10
On Sun, Oct 16, 2011 at 12:38:16PM +0200, Richard Guenther wrote:
> On Sun, Oct 16, 2011 at 7:30 AM, Andi Kleen <andi@firstfloor.org> wrote:
> > Andi Kleen <andi@firstfloor.org> writes:
> >
> >> From: Andi Kleen <ak@linux.intel.com>
> >>
> >> Use the Linux MADV_DONTNEED call to unmap free pages in the garbage
> >> collector.Then keep the unmapped pages in the free list. This avoid
> >> excessive memory fragmentation on large LTO bulds, which can lead
> >> to gcc bumping into the Linux vm_max_map limit per process.
> >
> > Could I have a decision on this patch please? The problem in PR50636
> > is still there and this is the minimum fix to fix it on Linux
> > as far as I know.
> >
> > If this patch is not the right way to go I would
> > appreciate some guidance on an alternative (but low cost)
> > implementation. Note I don't have capacity for any overly
> > complicated solutions.
> 
> The patch looks generally ok, but you are never giving back pages to the

It gives back pages, just not virtual address space. But I guess that is 
what you meant.

On the other hand this patch can actually give you more virtual
address space when you need large regions (>2 pages or so). 
The reason is that the old allocation pattern fragments the whole
address space badly and only leaves these small holes. With the madvise
patch that does not happen, ggc is all in a compacted chunk.

> system, and as we have other memory allocations that do not use the
> ggc pools you drain virtual memory on 32bit hosts.  Is any other patch
> in this series compensating for it?  If not I'd say we should munmap the
> pages when a full mapped range (2MB) is free.  Can you rename

I wrote such a patch initially, but ran into various problems, so 
I dropped it from the series. I can revisit it.

> 'unmapped' to 'discarded' please?  That would be less confusing.

Ok I can do that. 

Was that an approval?

-Andi
Richard Biener Oct. 17, 2011, 6:53 a.m. UTC | #11
On Sun, Oct 16, 2011 at 8:33 PM, Andi Kleen <andi@firstfloor.org> wrote:
> On Sun, Oct 16, 2011 at 12:38:16PM +0200, Richard Guenther wrote:
>> On Sun, Oct 16, 2011 at 7:30 AM, Andi Kleen <andi@firstfloor.org> wrote:
>> > Andi Kleen <andi@firstfloor.org> writes:
>> >
>> >> From: Andi Kleen <ak@linux.intel.com>
>> >>
>> >> Use the Linux MADV_DONTNEED call to unmap free pages in the garbage
>> >> collector.Then keep the unmapped pages in the free list. This avoid
>> >> excessive memory fragmentation on large LTO bulds, which can lead
>> >> to gcc bumping into the Linux vm_max_map limit per process.
>> >
>> > Could I have a decision on this patch please? The problem in PR50636
>> > is still there and this is the minimum fix to fix it on Linux
>> > as far as I know.
>> >
>> > If this patch is not the right way to go I would
>> > appreciate some guidance on an alternative (but low cost)
>> > implementation. Note I don't have capacity for any overly
>> > complicated solutions.
>>
>> The patch looks generally ok, but you are never giving back pages to the
>
> It gives back pages, just not virtual address space. But I guess that is
> what you meant.
>
> On the other hand this patch can actually give you more virtual
> address space when you need large regions (>2 pages or so).
> The reason is that the old allocation pattern fragments the whole
> address space badly and only leaves these small holes. With the madvise
> patch that does not happen, ggc is all in a compacted chunk.

Sure, but we do compete with the glibc heap with virtual memory usage
(I wonder if GGC should simply use malloc/free ...).  So I am worried
that we run out of address space earlier this way.  But I guess we can
revisit this when we run into actual problems ...

>> system, and as we have other memory allocations that do not use the
>> ggc pools you drain virtual memory on 32bit hosts.  Is any other patch
>> in this series compensating for it?  If not I'd say we should munmap the
>> pages when a full mapped range (2MB) is free.  Can you rename
>
> I wrote such a patch initially, but ran into various problems, so
> I dropped it from the series. I can revisit it.

Yes, please revisit it.  It should be as simple as scanning for a
large chunk in free_pages I suppose.

>> 'unmapped' to 'discarded' please?  That would be less confusing.
>
> Ok I can do that.
>
> Was that an approval?

Ok with the rename.

Thanks,
Richard.

> -Andi
>
diff mbox

Patch

diff --git a/gcc/config.in b/gcc/config.in
index f2847d8..e8148b6 100644
--- a/gcc/config.in
+++ b/gcc/config.in
@@ -1276,6 +1276,12 @@ 
 #endif
 
 
+/* Define to 1 if you have the `madvise' function. */
+#ifndef USED_FOR_TARGET
+#undef HAVE_MADVISE
+#endif
+
+
 /* Define to 1 if you have the <malloc.h> header file. */
 #ifndef USED_FOR_TARGET
 #undef HAVE_MALLOC_H
diff --git a/gcc/configure b/gcc/configure
index cb55dda..4a54adf 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -9001,7 +9001,7 @@  fi
 for ac_func in times clock kill getrlimit setrlimit atoll atoq \
 	sysconf strsignal getrusage nl_langinfo \
 	gettimeofday mbstowcs wcswidth mmap setlocale \
-	clearerr_unlocked feof_unlocked   ferror_unlocked fflush_unlocked fgetc_unlocked fgets_unlocked   fileno_unlocked fprintf_unlocked fputc_unlocked fputs_unlocked   fread_unlocked fwrite_unlocked getchar_unlocked getc_unlocked   putchar_unlocked putc_unlocked
+	clearerr_unlocked feof_unlocked   ferror_unlocked fflush_unlocked fgetc_unlocked fgets_unlocked   fileno_unlocked fprintf_unlocked fputc_unlocked fputs_unlocked   fread_unlocked fwrite_unlocked getchar_unlocked getc_unlocked   putchar_unlocked putc_unlocked madvise
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
diff --git a/gcc/configure.ac b/gcc/configure.ac
index a7b94e6..357902e 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -1027,7 +1027,7 @@  define(gcc_UNLOCKED_FUNCS, clearerr_unlocked feof_unlocked dnl
 AC_CHECK_FUNCS(times clock kill getrlimit setrlimit atoll atoq \
 	sysconf strsignal getrusage nl_langinfo \
 	gettimeofday mbstowcs wcswidth mmap setlocale \
-	gcc_UNLOCKED_FUNCS)
+	gcc_UNLOCKED_FUNCS madvise)
 
 if test x$ac_cv_func_mbstowcs = xyes; then
   AC_CACHE_CHECK(whether mbstowcs works, gcc_cv_func_mbstowcs_works,
diff --git a/gcc/ggc-page.c b/gcc/ggc-page.c
index 624f029..b0b3b3f 100644
--- a/gcc/ggc-page.c
+++ b/gcc/ggc-page.c
@@ -50,6 +50,10 @@  along with GCC; see the file COPYING3.  If not see
 #define USING_MALLOC_PAGE_GROUPS
 #endif
 
+#if defined(HAVE_MADVISE) && defined(MADV_DONTNEED)
+# define USING_MADVISE
+#endif
+
 /* Strategy:
 
    This garbage-collecting allocator allocates objects on one of a set
@@ -277,6 +281,9 @@  typedef struct page_entry
   /* The lg of size of objects allocated from this page.  */
   unsigned char order;
 
+  /* Unmapped page? */
+  bool unmapped;
+
   /* A bit vector indicating whether or not objects are in use.  The
      Nth bit is one if the Nth object on this page is allocated.  This
      array is dynamically sized.  */
@@ -740,6 +747,10 @@  alloc_page (unsigned order)
 
   if (p != NULL)
     {
+      if (p->unmapped)
+        G.bytes_mapped += p->bytes;
+      p->unmapped = false;
+
       /* Recycle the allocated memory from this page ...  */
       *pp = p->next;
       page = p->page;
@@ -956,7 +967,42 @@  free_page (page_entry *entry)
 static void
 release_pages (void)
 {
-#ifdef USING_MMAP
+#ifdef USING_MADVISE
+  page_entry *p, *start_p;
+  char *start;
+  size_t len;
+
+  for (p = G.free_pages; p; )
+    {
+      if (p->unmapped)
+        {
+          p = p->next;
+          continue;
+        }
+      start = p->page;
+      len = p->bytes;
+      start_p = p;
+      p = p->next;
+      while (p && p->page == start + len)
+        {
+          len += p->bytes;
+          p = p->next;
+        }
+      /* Give the page back to the kernel, but don't free the mapping.
+         This avoids fragmentation in the virtual memory map of the 
+ 	 process. Next time we can reuse it by just touching it. */
+      madvise (start, len, MADV_DONTNEED);
+      /* Don't count those pages as mapped to not touch the garbage collector
+         unnecessarily. */
+      G.bytes_mapped -= len;
+      while (start_p != p)
+        {
+          start_p->unmapped = true;
+          start_p = start_p->next;
+        }
+    }
+#endif
+#if defined(USING_MMAP) && !defined(USING_MADVISE)
   page_entry *p, *next;
   char *start;
   size_t len;