September 12, 2013 Re: Greedy memory handling | ||||
---|---|---|---|---|
| ||||
Posted in reply to H. S. Teoh | 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 |
Copyright © 1999-2021 by the D Language Foundation