[MLton] Crash fread(...) failed (only read 0) (Cannot allocate
memory) during deepFlatten with MLton 20070826
Matthew Fluet
fluet at tti-c.org
Sat May 31 20:14:47 PDT 2008
On Mon, 5 May 2008, Nicolas Bertolotti wrote:
> I tried a number of different solutions. It seems that moving the
> GC_generationalMaps struct to the GC_heap struct requires lots of
> changes in different parts of the code and would also lead to some
> useless computations when we manipulate the secondary heap (either
> adding some code to determine that we don't need to manipulate the
> card/cross map in that case or manipulate it anyway).
>
> I finally implemented a solution in which the data structures don't
> change and which is less intrusive.
For future reference, it is much easier to evaluate a patch that applies
cleanly to SVN HEAD.
After looking at your patch and considering alternatives, I have to agree
that moving the GC_generationalMaps struct into the GC_heap struct would
probably be fairly intrusive.
> While I was debugging this solution, it appeared that the function
> growHeap() may actually shrink the heap when the minimum size is less
> than the current size which, I guess, is not the expected behavior.
There is no bug in shrinking the size of the heap (under the original
code), but I agree that it is counter intuitive. If the desired heap size
is greater than the current heap size, then there seems to be no reason to
resize the heap to one smaller than the current heap size.
> Using this patch, I don't have any sporadic crashes when building my
> application (or MLton itself) on the 4 Gb debian box.
>
> I am interested in getting some feed-back about this patch so don't
> hesitate.
--- mlton/runtime/gc/heap.c 2008-04-11 19:12:27.000000000 +0200
+++ mltonp1/runtime/gc/heap.c 2008-05-05 01:37:57.000000000 +0200
@ -299,21 +335,26 @@
from = curHeapp->start + size;
to = newHeap.start + size;
remaining = size;
+ copyCardMapAndCrossMap(s, &newHeap);
+ GC_decommit(orig + curHeapp->size, computeCardMapAndCrossMapSize(s, curHeapp->size));
copy:
assert (remaining == (size_t)(from - curHeapp->start)
and from >= curHeapp->start
and to >= newHeap.start);
if (remaining < COPY_CHUNK_SIZE) {
GC_memcpy (orig, newHeap.start, remaining);
+ GC_release (orig, curHeapp->size);
} else {
+ size_t keep;
remaining -= COPY_CHUNK_SIZE;
from -= COPY_CHUNK_SIZE;
to -= COPY_CHUNK_SIZE;
GC_memcpy (from, to, COPY_CHUNK_SIZE);
- shrinkHeap (s, curHeapp, remaining);
+ keep = align (remaining, s->sysvals.pageSize);
+ GC_decommit (orig + keep, orig + curHeapp->size);
+ curHeapp->size = keep;
goto copy;
}
- releaseHeap (s, curHeapp);
newHeap.oldGenSize = size;
*curHeapp = newHeap;
} else {
This seem suspicious:
+ GC_decommit (orig + keep, orig + curHeapp->size);
The second argument should be the size of the region to be released. It
seems that the line should be more like:
+ GC_decommit (curHeapp->start + keep, curHeapp->size - keep);
which corresponds to the behavior of the shrinkHeap function in the
original code.
> Last point: the function growHeap() stores the current heap to disk when
> it can not allocate enough memory for the new heap. As this function is
> sometimes called with a minimum size that is less than the current heap
> size and this operation is quite costly, don't you think we could avoid
> it and keep the heap as is in that case?
It is not as simple as leaving the heap alone if we would otherwise page
to disk. Note that after failing to remap the current heap to a new size
(via remapHeap), the growHeap function shrinks the current heap (via
shrinkHeap (s, curHeapp, curHeapp->oldGenSize)) in order to free memory
for createHeap to allocate a heap of the desired size. If, while
retaining the live data in the current heap, we can't allocate a new heap
of the minimum size, then we can't simply keep the current heap, because
it will have been shrunk below the minimum size. (The minimum size will
be the size of the live data in the current heap plus some size for new
allocations.) Note that it would be futile to try to remap the current
heap back to the minimum size -- if the platform had mremap, then the
original remapHeap wouldn't have failed, because the minimum size is less
than the current heap, so after sufficient backoffs we would call mremap
with a new size less then the old size, which should succeed.
It would be possible to not shrink the current heap before attempting to
allocate a new heap of the desired size, but that runs the risk of not
freeing the memory that would allow us to allocate a heap of the desired
size.
More information about the MLton
mailing list