Ammo.js является портом известной библиотеки физического
движка Bullet Physics для JavaScript. В отличие от
управляемых языков, таких как Java или C#, в JavaScript память
управляется сборщиком мусора, однако Ammo.js использует
WebAssembly и объекты на его стороне требуют явного
освобождения. Игнорирование этого может привести к утечкам
памяти при работе с крупными сценами и динамическими
симуляциями.
Все объекты физического мира (btRigidBody,
btCollisionShape, btDynamicsWorld и т.д.)
создаются с помощью конструктора new Ammo.[Object]() и
хранятся в WebAssembly heap. JavaScript-объект лишь
ссылается на этот внутренний блок памяти. Когда объект больше не нужен,
простое удаление ссылки в JS не освобождает память в WebAssembly. Для
этого используется:
Ammo.destroy(object);
Ammo.destroy()dynamicsWorld.removeRigidBody(body);
Ammo.destroy(body.getCollisionShape());
Ammo.destroy(body);
dynamicsWorld.removeConstraint(constraint);
Ammo.destroy(constraint);
Ammo.js
часто возвращает временные объекты через методы, например
btVector3, btQuaternion. Если они создаются
вручную через new Ammo.btVector3(), их также следует
уничтожать:let tmpVec = new Ammo.btVector3(1, 0, 0);
// использование tmpVec
Ammo.destroy(tmpVec);
Ошибки в порядке освобождения объектов приводят к crash или неопределённому поведению. Рекомендуется следующая последовательность:
dynamicsWorld.removeRigidBody(body) или
dynamicsWorld.removeConstraint(constraint).btCollisionShape) или временные векторы.btRigidBody,
btConstraint).Пример правильного удаления тела с коллайдером:
let shape = body.getCollisionShape();
dynamicsWorld.removeRigidBody(body);
Ammo.destroy(body);
Ammo.destroy(shape);
При удалении большого количества объектов в сцене имеет смысл использовать циклы для освобождения всех тел и форм:
for (let i = dynamicsWorld.getNumCollisionObjects() - 1; i >= 0; i--) {
let obj = dynamicsWorld.getCollisionObjectArray().at(i);
let body = Ammo.castObject(obj, Ammo.btRigidBody);
let shape = body.getCollisionShape();
dynamicsWorld.removeRigidBody(body);
Ammo.destroy(body);
Ammo.destroy(shape);
}
Этот подход предотвращает накопление неосвобождённой памяти, особенно при генерации и удалении сотен объектов на каждой итерации симуляции.
Ammo.destroy() ссылка в JS остаётся, но объект уже
недействителен. Повторный вызов приведёт к ошибке.btCollisionShape.
Неочищенные формы создают «утечки» даже при удалении тела.btVector3,
btTransform) создаются часто для вычислений. Если они
создаются внутри цикла или функции, обязательно уничтожать их после
использования.Для упрощения управления памятью удобно создавать функции:
function destroyRigidBody(world, body) {
let shape = body.getCollisionShape();
world.removeRigidBody(body);
Ammo.destroy(body);
Ammo.destroy(shape);
}
Такой подход минимизирует ошибки и обеспечивает единообразное освобождение всех объектов.
new Ammo.[Object],
требует вызова Ammo.destroy() при завершении
использования.Правильное управление памятью через Ammo.destroy()
обеспечивает стабильность приложения, предотвращает
утечки и снижает нагрузку на браузер при длительных симуляциях с большим
количеством динамических объектов.