Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Monodroid: Performing a full GC

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);
}
like image 605
Leo Avatar asked Mar 25 '23 05:03

Leo


1 Answers

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.

like image 176
jonp Avatar answered Apr 01 '23 20:04

jonp