Monodroid:执行完整的GC

Leo*_*Leo 4 c# android garbage-collection xamarin.android

我尝试创建我的小粒子系统.我有粒子列表的粒子管理器,并在画布上绘制我的粒子.我只在init()函数中创建了一些像Paint等新对象!如果粒径<0,我将其删除:

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]);
        }
Run Code Online (Sandbox Code Playgroud)

然后我创建新的粒子并将其添加到我的列表中.我在日志中看到的内容:

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!
Run Code Online (Sandbox Code Playgroud)

然后我在渲染线程中有10-15(!!!)秒暂停!我阅读官方文档,但它没有任何解决方案.我分析并比较了我的代码与单声道JetBoy示例,但JetBoy的日志没有关于GC的任何内容.虽然我用JetBoy的例子编写了我的程序.如何修复完整的GC问题?


编辑: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
    }
Run Code Online (Sandbox Code Playgroud)

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);
    }
Run Code Online (Sandbox Code Playgroud)

Particle.cs

public void Draw(Canvas canvas) {
    mPaint.StrokeWidth = mSize;
    mPaint.Color = Color.Blue;
    canvas.DrawPoint(posX, posY, mPaint);
}
Run Code Online (Sandbox Code Playgroud)

jon*_*onp 7

主要问题是您有多个Java.Lang.Object实例同时存活,因为每个Java.Lang.Object实例都会导致JNI全局引用计数,这也会导致GC开销.想降低GC开销吗?减少您使用的GREF数量.

您可以通过启用gref日志记录来跟踪GREF计数

adb shell setprop debug.mono.log gref
Run Code Online (Sandbox Code Playgroud)

我假设这Particle是一个Java.Lang.Object子类,这意味着你至少有一个 particles.Count GREF活着.

解决的办法是不是Particle一个Java.Lang.Object子类,并改变你的架构以任何方式,以确保Particle没有一个.

如果Particle不是Java.Lang.Object实例,那么我们缺乏足够的信息来重现问题并建议解决方案.GREF日志输出将是一个方便的开始(多少GREFs 你一次?),因为它也将帮助你确定哪些正在创建的类型,所以你可以考虑减少其寿命.

有关读取gref日志输出的指南也可能很方便.