Greedy memory handling
Dmitry Olshansky
dmitry.olsh at gmail.com
Thu Sep 12 13:43:52 PDT 2013
13-Sep-2013 00:11, H. S. Teoh пишет:
> On Thu, Sep 12, 2013 at 11:13:30PM +0400, Dmitry Olshansky wrote:
>> 12-Sep-2013 20:51, H. S. Teoh пишет:
>>> On Thu, Sep 12, 2013 at 07:50:25PM +0400, Dmitry Olshansky wrote:
> [...]
>>>> Better option is to have finalizer hooked up to set some flag. Then
>>>> _after_ restoring the pointer we consult that flag variable.
>>>
>>> Good idea. The problem is, how to set a finalizer on a memory block
>>> that can change in size? The OP's original situation was that the
>>> buffer can be extended while in use, but I don't know of any D type
>>> that can associate a dtor with a ubyte[] array (note that the GC
>>> collecting the wrapper struct/class around the ubyte[] is not the
>>> same as collecting the actual memory block storing the ubyte[] -- the
>>> former can happen without the latter).
>>>
>>
>> Double indirection? Allocate a class that has finalizer, hold that
>> via weak-ref. The wrapper in turn contains a pointer to the buffer.
>> The interesting point then is that one may allocate said buffer via
>> C's realloc.
>>
>> Then once helper struct is collected the finalizer is called and
>> this is where we call free to cleanup C's heap.
>>
>> I'm thinking this actually is going to work.
> [...]
>
> Interesting idea, use C's malloc/realloc to hold the actual buffer. Only
> possible catch is, will that cause the GC to collect when it runs out of
> memory (which is the whole point of the OP's question)? I.e., does it
> make a difference in GC behaviour to allocate, say, 10MB from the GC vs.
> allocating 10MB from malloc/realloc?
The only problem I can foresee is that when it runs the collection
(*and* being tight on RAM) the C heap will not return said chunk back to
OS. Then GC won't pick up that memory, and we'd get out of ram.
I would safely assume however that for big buffers a mmap/munmap is
called (or its analogue) and hence memory is returned back to OS. That's
what all allocators do for huge chunks by anyway.
Otherwise we are still in a good shape, the memory will eventually be
freed, yet we get to reuse it quite cheaply in a tight loop. I don't
expect collections to run in these all that often ;)
>
> Assuming we have that settled, something like this should work:
>
> bool isValid;
> final class BufWrapper {
> void* ptrToMallocedBuf;
> this(void* ptr) {
> // We need this, 'cos otherwise we don't know if
> // our weak ref to BufWrapper is still valid!
> isValid = true;
>
> ptrToMallocedBuf = ptr;
> }
> ~this() {
> // If we're being collected, free the real
> // buffer too.
> free(ptrToMallocedBuf);
> isValid = false;
> }
> }
>
> // WeakPointer masks the pointer to BufWrapper in some suitable
> // way so that the GC will collect it when needed.
> WeakPointer!BufWrapper wrappedBufRef;
>
> void doWork(...) {
> void* buf;
Careful here - you really have first to get a pointer ... THEN check if
it's valid.
> if (!isValid) {
> buf = realloc(null, bufSize);
> wrappedBufRef.set(buf);
> } else {
//otherwise at this point GC.collect runs and presto, memory is freed
//too bad such a thing will never show up in unittests
> buf = wrappedBufRef.get();
> }
>
> // use buf here.
> }
Checking the flag should be somehow part of weak ref job.
I'd rather make it less error prone:
void* buf;
//unmask pointer, do the flag check - false means was freed
if(!weakRef.readTo(buf)){
//create & set new buf
buf = realloc(...);
}
... //use buf
weakRef.set(buf);
I think I'd code it up if nobody beats me to it as I need the same exact
pattern for std.regex anyway.
--
Dmitry Olshansky
More information about the Digitalmars-d-learn
mailing list