#java #performance #garbage-collection
#java #Производительность #сборка мусора
Вопрос:
На данный момент я разрабатываю игру для Android. Для этого все мои объекты имеют Vec3
позицию. Это выдержка из класса:
public class Vec3 {
public float x, y, z;
public Vec3(float x, float y, float z) {
this.x = x; this.y = y; this.z = z;
}
public Vec3 add(Vec3 v) {
return new Vec3(this.x v.x, this.y - v.y, this.z v.z);
}
public Vec3 sub(Vec3 v) {
return new Vec3(this.x - v.x, this.y - v.y, this.z - v.z);
}
}
Теперь предположим, что я хочу запустить этот код, где a
, b
и c
являются векторами из Vec3
класса:
a.add(b).sub(c);
Если я запускаю этот код со многими разными векторами, это приводит к задержкам в моей игре, потому что иногда, когда векторы больше не нужны, сборщик мусора удаляет их все одновременно. Я думал, что смогу решить проблему с помощью variable pool
но вот в чем проблема.. Я точно не знаю, когда вектор больше не используется в моей игре, поэтому я не знаю, где я должен проверить вектор.
Другая проблема заключается в том, что я генерирую много векторных объектов методом add и sub. Я хочу вычислять векторы, не изменяя их самостоятельно, вот почему я каждый раз возвращаю новый Vec3
. Допустим, я запускаю этот код:
a.add(b).add(b).add(b).add(b).add(b).sub(c);
При этом я генерирую шесть новых векторных объектов, но если вы подумаете об этом, мне нужен только один, чтобы вычислить векторы и сохранить результат без изменения исходных векторов. Это также проблема, которую я не могу решить с помощью variable pool
.
Есть ли у вас какие-либо идеи, как я могу эффективно решить эти проблемы?
Комментарии:
1. Просто проверка: вы определили, используя фактические данные о производительности, что ваши проблемы действительно связаны с накладными расходами на сборку мусора, и что эти объекты составляют значительную часть этих накладных расходов, верно? Я бы ожидал, что эти временные файлы будут собираться очень быстро и дешево, очень редко продолжаясь после одного поколения.
2. Да, я уверен, что эти проблемы возникают из-за накладных расходов на сборку мусора.
Ответ №1:
Вы можете писать методы на месте add
и sub
:
public Vec3 addInPlace(Vec3 v) {
x = v.x;
y = v.y;
z = v.z;
return this;
}
// and similarly for subInPlace
а затем используйте их, чтобы избежать создания дополнительных временных:
temp = a.add(b);
temp.addInPlace(b).addInPlace(b).addInPlace(b).addInPlace(b).subInPlace(c);
Вы должны управлять повторным использованием объекта явно, и вы теряете неизменяемость Vec3
объектов. Обе эти вещи отстой.
Ответ №2:
В вашем более сложном примере
a.add(b).add(b).add(b).add(b).add(b).sub(c);
вы можете устранить большую часть мусора, используя более мощную операцию
a.mulAdd(4, b, -1, c);
Я бы боялся времени умножения, но не сильно. Вы также могли бы написать специализированные операции, такие как
a.addFourSubOne(b, c);
что звучит довольно идиотски, поскольку вы никогда не сможете реализовать все операции, которые вы можете возможно использовать. OTOH вы можете легко реализовать все операции, которые вы действительно используете.
Я бы, вероятно, рекомендовал использовать изменяемые объекты. Вы можете предоставить для них операции как изменения, так и копирования, называемые чем-то вроде add
/ addInPlace
или plus
/ add
. Вы также могли бы реализовать как изменяемые, так и неизменяемые версии (это скоро надоедает, но вы можете справиться с этим, не внедряя ничего, что вам на самом деле не нужно).