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