I try to create my small particle system. I have ParticleManager with list of Particles and draw my particles on canvas. I create any new objects like Paint and etc once just in init() function! If particle size is < 0, I remove it:
for (int particle = 0; particle < particles.Count; particle++)
{
particles[particle].Update(); //particle size--;
if (!particles[particle].state) // size > 0 ? true : false
{
particles[particle] = null;
//here I tried all variations like
//((IDisposable)particles[particle]).Dispose();
//GC.SuppressFinalize(particles[particle]);
//System.GC.ReRegisterForFinalize(particles[particle]);
//((Java.Lang.Object)particles[particle]).Dispose(); and etc
particles.Remove(particles[particle]);
}
Then I create new Particle and add it to my list. What I see in my log:
GC cleanup summary: 1063 objects tested - resurrecting 1002.
GC cleanup summary: 1053 objects tested - resurrecting 992.
...
GC cleanup summary: 1052 objects tested - resurrecting 988.
46800 outstanding GREFs. Performing a full GC!
And then I have 10-15(!!!) second pause in my render thread!!! I read official documentation, but it hasn't any solution. I analysed and compared my code with mono JetBoy example, but JetBoy's log hasn't anything about GC. Although I wrote my program with JetBoy's example. How to fix full GC problem?
Edit: MainThread.cs
public override void Run()
{
Log.Verbose("Run()", "r");
Canvas c;
while (mRun) {
c = null;
mPassedTime = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
if (mTimerTask == null) {
mTimerTask = new CountDownTimerTask(this);
mTimer.Schedule(mTimerTask, mTaskIntervalInMillis);
}
try {
c = mSurfaceHolder.LockCanvas(null);
lock (mSurfaceHolder)
DoDrawRunning(c);
} finally {
if (c != null)
mSurfaceHolder.UnlockCanvasAndPost(c);
}
}
}
private void DoDrawRunning(Canvas canvas)
{
#region particles
for (int eng = 0; eng < engines.Count; eng++)
{
engines[eng].Update();
engines[eng].Draw(canvas);
}
#endregion
}
ParticleEnginee.cs
public void Update() {
if (particles.Count < maxTotal) {
for (int i = 0; i < total; i++) {
if (addNewB)
particles.Add(GenerateNewParticle()); // return new Particle
}
}
for (int particle = 0; particle < particles.Count; particle++) {
particles[particle].Update(); // position and size update
if (!particles[particle].state) // size > 0 ?
particles.RemoveAt(particle);
}
}
public void Draw(Canvas canvas) {
for (int j = 0; j < 3; j++) // 3 particle color-levels draw
for (int index = 0; index < particles.Count; index++)
particles[index].Draw(canvas, j);
}
Particle.cs
public void Draw(Canvas canvas) {
mPaint.StrokeWidth = mSize;
mPaint.Color = Color.Blue;
canvas.DrawPoint(posX, posY, mPaint);
}
The primary problem is that you have too many Java.Lang.Object
instances alive at once, as each Java.Lang.Object
instance contributes to the JNI global reference count, which also contributes to GC overhead. Want to reduce GC overhead? Reduce the number of GREFs you use.
You can track the GREF count by enabling gref logging
adb shell setprop debug.mono.log gref
I assume that Particle
is a Java.Lang.Object
subclass, meaning you have at least particles.Count
GREFs alive at once.
The solution is to not make Particle
a Java.Lang.Object
subclass, and alter your architecture in any way to ensure that Particle
isn't one.
If Particle
isn't a Java.Lang.Object
instance, then we lack sufficient information to reproduce the problem and suggest a solution. GREF logging output would be a handy start (how many GREFs do you have at once?), as it will also help you determine which types are being created so you can consider reducing their lifetime.
This guide on reading gref log output may also be handy.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With