www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Can't free memory on exiting?

reply breezes <wangyuanzju gmail.com> writes:
I wrote the following program to test malloc/free:

import std.stdio;
import core.memory;

class ExternalMemoryHandle {
	void *p;

	this() {
		p = GC.malloc(1024);
	}

	~this() {
		GC.free(p);
	}
}

void main() {
	auto test = new ExternalMemoryHandle();
	writeln("exiting");
}


The philosophy of this program is use class ExternalMemoryHandle as a handle
to some manually managed memory. If a ExternalMemoryHandle is constructed, it
alloc some memory. When this ExternalMemoryHandle is garbage collected, it
free those memory.

However it will fail with OutOfMemoryError. I got the following error when
running this program:

exiting
core.exception.OutOfMemoryError

What's wrong with this tiny program?
Oct 31 2011
parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
I've had something similar happen, yebblies explained it best:

http://d.puremagic.com/issues/show_bug.cgi?id=6821

For that issue it was:

"Currently attempting to allocate during a garbage collection throws an oom
error.  This is due to limitations in the current gc implementation, previously
it just corrupted memory.  As the Foo object is only destroyed when the final
collection occurs, the assert failing in the destructor tries to allocate an
AssertError and fails."

You should use core.stdc.stdlib.malloc if you want to use the C
runtime and manage memory manually, not GC.malloc which uses the
garbage collector. IOW:

import std.stdio;
import core.stdc.stdlib;

class ExternalMemoryHandle {
       void *p;

       this() {
               p = malloc(1024);
       }

       ~this() {
               free(p);
       }
}

void main() {
       auto test = new ExternalMemoryHandle();
       writeln("exiting");
}

Otherwise if you want to use the GC there's no point in calling
GC.free in the destructor since the GC will do that automatically.
Oct 31 2011
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Andrej Mitrovic:

 Otherwise if you want to use the GC there's no point in calling
 GC.free in the destructor since the GC will do that automatically.
And if the OP wants some determinism in the dellocation of the memory allocated with malloc, the OP has to use a struct (scope classes used to give the same determinism, but they are deprecated). Bye, bearophile
Oct 31 2011
prev sibling parent reply breezes <wangyuanzju gmail.com> writes:
Thanks Andrej. That bug says that you can not alloc memory during GC. However i
don't alloc but free memory in ~this. But anyway, as you said, I should use
malloc/free in core.stdc.stdlib to manage memory manually. I modified the code
to
use that malloc/free, and it works without crashing.

However, the following little bit more complex test got a Bus error.

import core.stdc.stdlib;

class Allocator {
	void* alloc(size_t size) {
		return malloc(size);
	}

	void free(void *block) {
		core.stdc.stdlib.free(block);
	}
}

class Pages {
	this(Allocator allocator) {
		_allocator = allocator;
		void *p = _allocator.alloc(1000);
		_pages ~= p;
	}

	~this() {
		_allocator.free(_pages[0]);// Bus error there
	}

	Allocator	_allocator;
	void*[]		_pages;
	size_t		_a;
	size_t		_b;
	size_t		_c;
	size_t		_d;
	size_t		_e;
	size_t		_f;
}

void main() {
	auto a = new Allocator();
	auto pg = new Pages(a);
}

I got the following bus error:
Bus error: 10

What's the problem? Does it mean that I can not access _allocator during the
deconstruction of pg? And the most mythical thing is that if I comment out the
declaration of _a to _f of Pages, then the bus error will go.

(I use the most recent dmd 2.0.056.)
Nov 01 2011
parent Denis Shelomovskij <verylonglogin.reg gmail.com> writes:
01.11.2011 10:27, breezes пишет:
 Thanks Andrej. That bug says that you can not alloc memory during GC. However i
 don't alloc but free memory in ~this. But anyway, as you said, I should use
 malloc/free in core.stdc.stdlib to manage memory manually. I modified the code
to
 use that malloc/free, and it works without crashing.

 However, the following little bit more complex test got a Bus error.

 import core.stdc.stdlib;

 class Allocator {
 	void* alloc(size_t size) {
 		return malloc(size);
 	}

 	void free(void *block) {
 		core.stdc.stdlib.free(block);
 	}
 }

 class Pages {
 	this(Allocator allocator) {
 		_allocator = allocator;
 		void *p = _allocator.alloc(1000);
 		_pages ~= p;
 	}

 	~this() {
 		_allocator.free(_pages[0]);// Bus error there
 	}

 	Allocator	_allocator;
 	void*[]		_pages;
 	size_t		_a;
 	size_t		_b;
 	size_t		_c;
 	size_t		_d;
 	size_t		_e;
 	size_t		_f;
 }

 void main() {
 	auto a = new Allocator();
 	auto pg = new Pages(a);
 }

 I got the following bus error:
 Bus error: 10

 What's the problem? Does it mean that I can not access _allocator during the
 deconstruction of pg? And the most mythical thing is that if I comment out the
 declaration of _a to _f of Pages, then the bus error will go.

 (I use the most recent dmd 2.0.056.)
From http://d-programming-language.org/class.html#destructors (this page is a bit broken now): "The garbage collector is not guaranteed to run the destructor for all unreferenced objects. Furthermore, the order in which the garbage collector calls destructors for unreference objects is not specified. This means that *when the garbage collector calls a destructor for an object of a class that has members that are references to garbage collected objects, those references may no longer be valid*. This means that *destructors cannot reference sub objects*. This rule does not apply to auto objects or objects deleted with the DeleteExpression, as the destructor is not being run by the garbage collector, meaning all references are valid." Make your `free` function static to avoid problem that it needs `this` pointer (of course it may work with damaged `this` but it isn't guaranteed). And your pages array should be allocated with `malloc` too (`~=` allocates in GC heap of course).
Nov 01 2011