【问题标题】:Monodroid: Performing a full GCMonodroid:执行完整的 GC
【发布时间】:2013-04-24 07:19:58
【问题描述】:

我尝试创建我的小粒子系统。我有带有粒子列表的 ParticleManager 并在画布上绘制我的粒子。我只需在 init() 函数中创建任何新对象,如 Paint 等!如果粒度

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]);
        }

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

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!

然后我的渲染线程中有 10-15(!!!) 秒的暂停!!!我阅读了官方文档,但没有任何解决方案。我分析并比较了我的代码与单 JetBoy 示例,但 JetBoy 的日志没有关于 GC 的任何内容。虽然我用 JetBoy 的例子写了我的程序。 如何解决full 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
    }

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);
    }

粒子.cs

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

【问题讨论】:

  • 您是否尝试过定期手动调用 GC.Collect()? (例如在您的渲染线程中)请参阅docs.xamarin.com/guides/android/advanced_topics/…
  • 你应该为你的粒子使用弱 WeakReference 类
  • @Julien,是的,它给了我延迟。致格林西:好主意,我会试试的

标签: c# android garbage-collection xamarin.android


【解决方案1】:

主要问题是您有太多的Java.Lang.Object 实例同时处于活动状态,因为每个Java.Lang.Object 实例都会增加 JNI 全局引用计数,这也会增加 GC 开销。想要减少 GC 开销?减少您使用的 GREF 数量。

您可以通过enabling gref logging 跟踪 GREF 计数

adb shell setprop debug.mono.log gref

我假设ParticleJava.Lang.Object 的子类,这意味着您至少同时拥有particles.Count GREF。

解决方案是使Particle 成为Java.Lang.Object 的子类,并以任何方式更改您的架构以确保Particle 不是一个。

如果Particle 不是Java.Lang.Object 实例,那么我们缺乏足够的信息来重现问题并提出解决方案。 GREF 日志输出将是一个方便的开始(您一次有多少个 GREF?),因为它还可以帮助您确定正在创建哪些类型,以便您可以考虑缩短它们的生命周期。

This guide on reading gref log output 也可能很方便。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-02
    • 2016-02-23
    • 1970-01-01
    相关资源
    最近更新 更多