D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 7095 - GC doesn't return or reuse unused memory [D2.056]
Summary: GC doesn't return or reuse unused memory [D2.056]
Status: RESOLVED INVALID
Alias: None
Product: D
Classification: Unclassified
Component: druntime (show other issues)
Version: D2
Hardware: x86_64 Mac OS X
: P2 normal
Assignee: No Owner
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-12-11 08:59 UTC by Heywood Floyd
Modified: 2011-12-11 10:38 UTC (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Heywood Floyd 2011-12-11 08:59:32 UTC
The garbage collector refuses to collect garbage. MacOS X 10.7.2, DMD v2.056


import core.memory;
void main(){
	long[] arr;
	foreach(i;0..100){
		arr = new long[10_000_000];
		GC.collect();
		GC.minimize();
	}
}


$ rdmd gcrun.d
core.exception.OutOfMemoryError
$ _
Comment 1 David Simcha 2011-12-11 09:47:56 UTC
D's GC isn't fully precise yet.  In other words, it can't always tell the difference between a pointer and a non-pointer.  When you allocate an 80-megabyte array, the probability that some non-pointer will look like it points into the array if interpreted as a pointer is pretty high.  

This is a fundamental limitation of the current garbage collector design, rather than a bug.  As a workaround, it's best to use C's malloc for very large arrays.
Comment 2 Heywood Floyd 2011-12-11 10:01:25 UTC
I understand. Thanks for the reply!

(With risk of polluting the bug tracker here, but perhaps to clarify the workaround: Could GC.malloc/free be used as well? Also, I assume if a large array of structs that in turn contain references to other heap objects, those would not get scanned had the array been C-alloced risking active objects to get collected?)
Comment 3 David Simcha 2011-12-11 10:12:27 UTC
(In reply to comment #2)
> I understand. Thanks for the reply!
> 
> (With risk of polluting the bug tracker here, but perhaps to clarify the
> workaround: Could GC.malloc/free be used as well? 

You could use GC.free().  It's somewhat dangerous, though, because you risk corrupting the GC if you free something incorrectly.  

> Also, I assume if a large
> array of structs that in turn contain references to other heap objects, those
> would not get scanned had the array been C-alloced risking active objects to
> get collected?)

Use GC.addRange() to get around this.
Comment 4 deadalnix 2011-12-11 10:27:18 UTC
(In reply to comment #1)
> D's GC isn't fully precise yet.  In other words, it can't always tell the
> difference between a pointer and a non-pointer.  When you allocate an
> 80-megabyte array, the probability that some non-pointer will look like it
> points into the array if interpreted as a pointer is pretty high.  
> 
> This is a fundamental limitation of the current garbage collector design,
> rather than a bug.  As a workaround, it's best to use C's malloc for very large
> arrays.

Isn't GC has a flag that says if something may contains pointer or not ?

Why this flag is set when allocation an array of long ?
Comment 5 David Simcha 2011-12-11 10:38:41 UTC
(In reply to comment #4)
> Isn't GC has a flag that says if something may contains pointer or not ?
> 
> Why this flag is set when allocation an array of long ?

It's not.  If a bit pattern in `arr` would point into GC memory if interpreted as a pointer, then the pattern is ignored.  The GC has some level of precision in that it knows whether a heap-allocated block contains any pointers or not.  However, the issue is that pointers **from** somewhere else point **at** arr, keeping it alive incorrectly.  

There are two sources for false pointers:

1.  The stack, where the GC has no knowledge whatsoever of what's a pointer and what isn't.  I think this also applies to the static data segment.

2.  Heap allocated data structures that contain both pointer and non-pointer data, e.g.:

struct Foo {
    void* ptr;
    size_t num;
}

If you allocate a Foo on the heap, the whole block will be marked as potentially containing pointers.  The GC won't know what parts of it aren't pointers.  `num` could have a bit pattern that would be a valid pointer and will keep some objects alive unnecessarily.