Memory management when frequently deleting dynamicsWorld

Post Reply
hiker
Posts: 83
Joined: Tue Oct 24, 2006 11:52 pm
Location: Australia
Contact:

Memory management when frequently deleting dynamicsWorld

Post by hiker »

Hi,

quick question: for the kart racing game I am working on I am creating a new btDiscreteDynamics world for each race. I am currently having some problems with deleting a dynamics world (crashes somewhere in the destructor). For now I assume that I am doing something wrong and I will chase it for a while, but I am generally wondering about memory management: do I have to free all allocated objects (like btAxisSweep3, btSequentialImpulseConstraintSolver, rigid bodies, motion states for bodies, ...) myself? Or are any objects handled (esp. freed) internally?

When can I delete objects, e.g. btAxisSweep - immediately after the dynamics world was created (i.e. does bullet make a copy), or do I have to keep it till the end (i.e. delete it after deleting the world)?

Or would you generally recommend to have only one dynamics world, and just remove all btrigid objects for each new race?

Hope these questions are not too general.

Cheers,
Joerg
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA
Contact:

Re: Memory management when frequently deleting dynamicsWorld

Post by Erwin Coumans »

You should only delete what you allocate, see the BasicDemo for example.
hiker wrote:do I have to free all allocated objects (like btAxisSweep3, btSequentialImpulseConstraintSolver, rigid bodies, motion states for bodies, ...) myself? Or are any objects handled (esp. freed) internally?
You have to deal with all those yourself.
When can I delete objects, e.g. btAxisSweep - immediately after the dynamics world was created (i.e. does bullet make a copy), or do I have to keep it till the end (i.e. delete it after deleting the world)?
Unless stated otherwise, there is no copy made internally, so you should not delete them. When a rigid body is added to the world, you should not delete it, until you after you removed it.
In general, delete objects in reverse order of creation, for example see BasicDemo:

Code: Select all

void	BasicDemo::exitPhysics()
{
	//cleanup in the reverse order of creation/initialization

	//remove the rigidbodies from the dynamics world and delete them
	int i;
	for (i=m_dynamicsWorld->getNumCollisionObjects()-1; i>=0 ;i--)
	{
		btCollisionObject* obj = m_dynamicsWorld->getCollisionObjectArray()[i];
		btRigidBody* body = btRigidBody::upcast(obj);
		if (body && body->getMotionState())
		{
			delete body->getMotionState();
		}
		m_dynamicsWorld->removeCollisionObject( obj );
		delete obj;
	}

	//delete collision shapes
	for (int j=0;j<m_collisionShapes.size();j++)
	{
		btCollisionShape* shape = m_collisionShapes[j];
		delete shape;
	}

	//delete dynamics world
	delete m_dynamicsWorld;

	//delete solver
	delete m_solver;

	//delete broadphase
	delete m_overlappingPairCache;

	//delete dispatcher
	delete m_dispatcher;

	delete m_collisionConfiguration;

	
}
Or would you generally recommend to have only one dynamics world, and just remove all btrigid objects for each new race?
It is a good idea to delete the simulation and start from scratch for each race.

Hope this helps,
Erwin

By the way, there are some internal memory caches for collision pairs, contact points and so on. Those internal allocations are freed internally (all using btAlignedAlloc/btAlignedFree, some using pools or stack allocators to avoid fragmentation etc). Bullet 2.64 has a memory leak tool for internal SDK data allocations, run AllBulletDemos and select MemoryLeakChecker. This only applies to btAlignedAlloc/Free.
hiker
Posts: 83
Joined: Tue Oct 24, 2006 11:52 pm
Location: Australia
Contact:

Re: Memory management when frequently deleting dynamicsWorld

Post by hiker »

Hi Erwin,
Erwin Coumans wrote:You should only delete what you allocate, see the BasicDemo for example.
Ah thanks. I always looked at the VehicleDemo, where the memory is not freed (I know that this is not important for the demo, I just thought with me having problems deleting world at the end, perhaps I am doing something wrong in freeing the data structures).
Erwin Coumans wrote: Unless stated otherwise, there is no copy made internally, so you should not delete them. When a rigid body is added to the world, you should not delete it, until you after you removed it.
In general, delete objects in reverse order of creation, for example see BasicDemo:
...
Understood.
Erwin Coumans wrote:It is a good idea to delete the simulation and start from scratch for each race.
OK, at least I was doing it the right way. OK, then I'll start debugging my problem :)

Thanks a lot!
Joerg
ngaloppo
Posts: 11
Joined: Wed Dec 06, 2006 1:59 am
Location: Chapel Hill, NC

Re: Memory management when frequently deleting dynamicsWorld

Post by ngaloppo »

Erwin Coumans wrote:In general, delete objects in reverse order of creation, for example see BasicDemo:
Hmm, is this a design decision? I've grown accustomed to using reference counted pointers to manage and pass around most non-time-critical allocated objects, making the bookkeeping of what is alive and what not more manageable. Of course, that means that you have to take this into account when writing your class destructors, not making any assumptions about who allocated what.

You make it sound that such assumptions *are* made in Bullet, therefore requiring you to delete in some particular order. I can see where this becomes a problem, e.g. when exceptions are thrown, or early exits are required and you don't know the current state of your program.
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA
Contact:

Re: Memory management when frequently deleting dynamicsWorld

Post by Erwin Coumans »

ngaloppo wrote:
Erwin Coumans wrote:In general, delete objects in reverse order of creation, for example see BasicDemo:
Hmm, is this a design decision? I've grown accustomed to using reference counted pointers to manage and pass around most non-time-critical allocated objects, making the bookkeeping of what is alive and what not more manageable.
Yes indeed, it was my choice to not use reference counting, and to leave it up to the user to allocate and de-allocate the public objects, mainly for its simplicity.

I think there are pros and cons for both reference counting and single ownership. It is a great topic for long discussions. I've used reference counting extensively in the past, so here are my reasons for not using it, and using single-owner ship and checking for null pointers instead.

1) Reference counting creates dependencies, checking for a null pointer doesn't.

I'm a big fan of keeping things simple, modular and re-usable, with as few dependencies as possible. Once you introduce reference counting (addRef/releaseRef), the reference counting headerfile propagates through many files and projects, making it more difficult to re-use small parts of the code. A similar problem (not being able to re-use code) occurs due to lack of standard math classes. For clean interfaces, I prefer using standard C types. This contradics the fact that I used C++ for the Bullet interface. Providing C++ opens up the library for more customizations/re-use/modification.

2) Reference counting looses control of memory usage.
In demanding applications, especially on consoles with limited memory, it is important to keep control and insight of memory usage. With reference counting it is not clear if/when memory is available. When you use the single ownership, it is clear when it is safe to delete all data in one stage. This is not just a local problem, but also a global problem cross modules. I've had a case where we had a C++ and Python object binding, mixing python reference counting to our C++ reference counting. Once users created a global python variable, referencing the C++ object, we couldn't terminate the python interpreter for that reason.

3) Reference counting has a large impact on the system, especially container classes and serialization and circular dependencies. When using single ownership and pointers, container classes simply store the pointer, and don't need to know about reference counting. This point is similar to point 1 by the way.

4) Reference counting can have an impact on the performance, due to many (recursive) addRef/releaseRef calls. Keeping strict rules for the object life time (a rigid body is always valid as long as it is added to the dynamics world, so within the SDK, no null pointer checks or releaseRef are needed).

5) Reference counting can be very errorprone for users.
It is not always clear to users which objects are reference counted, and what the dependencies are. Some pointers to objects needs a 'delete' and others need a 'releaseRef'. Simply checking for a null pointer, unless you are the owner is more manageable.
You make it sound that such assumptions *are* made in Bullet, therefore requiring you to delete in some particular order. I can see where this becomes a problem, e.g. when exceptions are thrown, or early exits are required and you don't know the current state of your program.
I agree with you, the user needs to know the state of the program. To make it a bit easier, Bullet doesn't use exception handling, nor run-time type information, and there are no early exits possible, you have to go through the standard process of destructing objects in the reverse order they got created.

I agree it should be better documented that the responsibility to free objects is up to the owner/creator in the reverse order.

Does this clarifies things?
Erwin
AlexSilverman
Posts: 141
Joined: Mon Jul 02, 2007 5:12 pm

Re: Memory management when frequently deleting dynamicsWorld

Post by AlexSilverman »

It is a good idea to delete the simulation and start from scratch for each race.
Really? Why is this? I've been seeing some inconsistent strange behavior (mentioned here), but I just chalked it up to the fact that I'm on a console and haven't been able to set up a proper test bed in our engine. Are there any reproducible issues that emerge from adding and removing bodies from the same simulation across multiple races (sticking with the original example)?

- Alex
Last edited by AlexSilverman on Thu Nov 08, 2007 3:55 pm, edited 3 times in total.
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA
Contact:

Re: Memory management when frequently deleting dynamicsWorld

Post by Erwin Coumans »

AlexSilverman wrote:
Or would you generally recommend to have only one dynamics world, and just remove all btrigid objects for each new race?
Really? Why is this? I've been seeing some inconsistent strange behavior (mentioned here), but I just chalked it up to the fact that I'm on a console and haven't been able to set up a proper test bed in our engine. Are there any reproducible issues that emerge from adding and removing bodies from the same simulation across multiple races (sticking with the original example)?

- Alex
If you want determinism/reproducability, it is better to create a new dynamics world, and create the objects in exactly the same order. Bullet maintains internal caches, and the order depends on the order that objects are added, moved and removed.

To give some background: the order in which the constraint solver iterates over the individual constraints has an impact on the result. This order depends on the order of the overlapping pair array. In a similar fashion, the contact points for each collision pair depends on the order each point gets added.

We should add some mechanism to clear/sort all internal caches (overlapping pairs, persistent contact points, warm starting values and so on) in a predictable manner, but that is outstanding. In the AllBulletDemos, you can notice the difference between re-starting a demo by pressing 'R' key (which destroys the demo and re-creates it), or using the space-key (which teleports objects back to the original location, and flushes some caches, but not exhaustively, and resets a random seed)

Hope this helps,
Erwin
AlexSilverman
Posts: 141
Joined: Mon Jul 02, 2007 5:12 pm

Re: Memory management when frequently deleting dynamicsWorld

Post by AlexSilverman »

I edited my original post, since I quoted the wrong bit, but it seems like you saw through my mistake. Your answer clears it up a lot though. I thought there were drifting values or something somehow. Good to know that's not the case :)

Thanks for the response.

- Alex
hiker
Posts: 83
Joined: Tue Oct 24, 2006 11:52 pm
Location: Australia
Contact:

Re: Memory management when frequently deleting dynamicsWorld

Post by hiker »

Hi all,

just in case that someone else has the same problem (crashes when deleting a dynamics world): in my case it was caused by inconsistent memory releases: rigid bodies where still included in the dynamics world, but the collision shapes had been freed - stupid mistake actually :( Hope that this bit of information might save someone debugging time :)

Thanks Erwin for your long explanation and reasoning for not using ref counting - highly appreciated.

Cheers,
Joerg
roar
Posts: 3
Joined: Thu Aug 11, 2005 11:34 pm

Re: Memory management when frequently deleting dynamicsWorld

Post by roar »

If I need to delete objects in reversed order how should I handle things like pickups and dynamically generated content?

Say I create 10 spheres and when I hit the thing I want to delete it.
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA
Contact:

Re: Memory management when frequently deleting dynamicsWorld

Post by Erwin Coumans »

roar wrote:If I need to delete objects in reversed order how should I handle things like pickups and dynamically generated content?

Say I create 10 spheres and when I hit the thing I want to delete it.
The reverse order applies to dynamics world , collision dispatcher, constraint solver, broadphase, collision objects (rigid bodies), collision shapes etc. Within the collection of rigid bodies, it doesn't matter which order, so you can just iterate over the bodies. Check the Bullet/Demos/BasicDemo to see an example of deleting objects in reverse order.

Hope this helps,
Erwin
roar
Posts: 3
Joined: Thu Aug 11, 2005 11:34 pm

Re: Memory management when frequently deleting dynamicsWorld

Post by roar »

I'll freely admit to not reading much of the code or documentation, I just went through some samples and the userguide...

Is the following correct?

Given that
- btRigidBody derives from btCollisionObject
- m_dynamicsWorld has a collection of btCollisionObjects, m_collisionObjects (Rigid bodies and other collision objects)
- Stuff in m_collisionObjects must be deleted in reverse order

Say I create a bunch of rigid bodies and keep their pointers in an array

Code: Select all

{
	btBoxShape* shape = new btBoxShape(btVector3(0.5f,0.1f,0.5f));
	btTransform tm(btTransform::getIdentity());
	btRigidBody* bodies[10];
	for( int i=0; i<10; ++i )
	{
		bodies[i] = localCreateRigidBody(0.0f, tm, shape);
	}
}
can I then, when something happens, do (during simulation, not when exiting the program)

Code: Select all

{
	btRigidBody* body = bodies[5];
	if (body && body->getMotionState())
	{
		delete body->getMotionState();
	}
	m_dynamicsWorld->removeCollisionObject( body );
	bodies[5] = NULL;
}
Post Reply