【问题标题】:Verify why object is only garbage collected on second garbage collection run验证为什么对象只在第二次垃圾收集运行时被垃圾收集
【发布时间】:2020-03-29 19:23:39
【问题描述】:

出于学习目的,我目前正在试验垃圾收集(Pixel 2,Android 10 -> ART)。我对以下实验的观察是,CountActivity 在第一次显式垃圾收集运行时永远不会被删除,而只会在第二次显式垃圾收集运行时被删除。我想了解为什么它在第一次运行时没有被删除。我认为这是由于某种分代垃圾收集,但我想验证它。我如何“观察”垃圾收集的作用?例如。是否可以看到垃圾收集对堆进行分组的内部代?

我的测试示例如下:

  1. MainActivity,显示一个按钮“显示 CountActivity”
  2. CountActivity 当按下MainActivity 中的“显示计数活动”按钮时显示。
  3. 我使用系统返回键结束CountActivity

在此之后,我强制运行显式垃圾回收,捕获一个堆。 CountActivity还在。在强制运行第二次显式垃圾回收并再次捕获堆后,CountActivity 消失了。现在我仍然想了解为什么需要运行两次垃圾回收。

MainActivity

class MainActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    setTheme(R.style.AppTheme)
    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)

    buttonStart.setOnClickListener {
      showCountActivity()
    }

  }

  private fun showCountActivity() {
    val intent = Intent(this, CountActivity::class.java)
    startActivity(intent)
  }

}

CountActivity

class CountActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_count)
  }
}

【问题讨论】:

  • 能否分享您的 GC 日志文件,以便我们了解实际发生的情况?
  • 当然。你能给我一些提示如何收集这些吗?猜你不只是在谈论 logcat 条目,对吧?
  • 当你已经有一个堆转储时,你应该能够找出对对象的哪些引用确实存在。
  • 我没有理由假设堆转储是相同的。事实上,你已经说过它们不是,因为至少在所讨论对象的存在方面存在差异。当第一次转储中没有对它的引用时,我们不仅涉及垃圾收集器的工作方式,还涉及堆转储的创建方式。除了能够看到堆转储中的对象之外,还有其他后果吗?
  • @Holger 不,没有其他后果。它存在于第一个堆转储中,然后在第二个中消失,中间没有任何操作。

标签: android garbage-collection


【解决方案1】:

ART(Android 运行时)使用所谓的 generational garbage collector,它基本上根据创建对象的时间创建对象桶(理论上,较新的对象更有可能需要进行垃圾回收,因为这就是所有临时对象(构建器、其他中间对象等)将结束的地方。因此,垃圾收集不一定会收集所有代的对象。

以下是有关 Android 垃圾收集器工作原理的一些基本信息:https://developer.android.com/topic/performance/memory-overview

您可以通过在 Android Studio 中获取和分析堆转储来验证这一点,并注意没有从相关对象到任何 GC 根的路径。

这里有一些关于 GC 细节的非常详细的文档(更可能对从事 ART 本身的工作的人有用,但仍然提供信息 - ergonomics 部分特别讨论了世代收集):https://source.android.com/devices/tech/dalvik/gc-debug

【讨论】:

  • 谢谢,但有没有办法真正检查是否是这种情况?我真的很想看看(不是假设,虽然我也相信这是由于代代相传)运行了哪种类型的 GC,如果可能的话,哪个对象在哪个桶中。
  • @stefan.at.wpf 我可以想象最简单的方法是编写一个小型测试程序并使用内置工具(如 IDE 的调试器)
  • @riggedCoinflip 你不会在调试器中看到它。 GC 的某些部分在 systrace 中是可见的,但是如果没有更深入的知识就很难解释。此外,它似乎只显示运行了哪种类型的 GC,而不是堆的外观。
  • 好的,谢谢你的澄清,我不知道。到目前为止我从来没有担心过垃圾收集
  • 不过,您应该能够通过堆转储进行检查。
【解决方案2】:

分析垃圾收集的确切类型和工作最好通过分析它写入的日志来完成,可以通过 gceasy.io 网站上的免费工具自动分析,在他们的博客中说明了这一点:

https://blog.gceasy.io/2017/05/09/understanding-android-gc-logs/

如果您从日志中得知您遇到了需要进一步调查的问题(例如:内存泄漏、性能问题),您可能需要创建一个堆转储并分析您的所有对象和对象引用选择例如自定义代码:

https://rhye.org/post/advanced-android-heap-analysis/

或者也许通过 ahat 之类的网络应用程序:

https://android.googlesource.com/platform/art/+/master/tools/ahat/README.txt

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-10-12
  • 1970-01-01
  • 2010-12-19
  • 2014-08-08
  • 2017-05-17
  • 1970-01-01
  • 2010-11-08
相关资源
最近更新 更多