垃圾收集器没有像 Android 应用程序中那样释放“垃圾内存”

2024-01-11

Hello!

我是一名 Java 和 Android 开发新手,最近在处理应用程序的内存管理方面遇到了麻烦。我将把本文分成几个部分,以使其更清晰易读。


我的应用程序的简要说明

这是一个由多个阶段(级别)组成的游戏。每个阶段都有一个玩家的起点和一个出口,引导玩家进入下一个阶段。每个阶段都有自己的一组障碍。目前,当玩家到达最后阶段(到目前为止我只创建了 4 个阶段)时,他/她会自动返回到第一阶段(1 级)。

一个名为的抽象类游戏对象(延伸Android.View)定义了玩家以及游戏中存在的所有其他对象(障碍物等)的基本结构和行为。所有对象(本质上是视图)都在我创建的自定义视图中绘制(扩展了 FrameLayout)。游戏逻辑和游戏循环由副线程(gameThread)处理。这些阶段是通过从 xml 文件检索元数据来创建的。

问题

除了我的代码中所有可能的内存泄漏(所有这些我一直在努力寻找和解决)之外,还有一个与垃圾收集器相关的奇怪现象发生。我不会用文字来描述它,以免让你感到困惑,而是使用图像。正如孔子所说:“形象胜千言”。好吧,在这种情况下,我刚刚让您免于阅读 150,000 个单词,因为下面我的 GIF 有 150 帧。

描述:第一个图像代表首次加载“第一阶段”时我的应用程序的内存使用情况。第二张图片 (GIF) 首先代表第二次加载“第一阶段”时我的应用程序的内存使用时间线(如前所述,当玩家击败最后一个阶段时会发生这种情况),然后是四个垃圾收集是我强行发起的。

您可能已经注意到,两种情况之间的内存使用量存在巨大差异(几乎 50MB)。首次加载“Stage 1”时,游戏启动时,应用程序使用 85MB 内存。当第二次加载同一个stage时,稍晚一点,内存使用量已经是130MB了!这可能是由于我的编码不当造成的,因此我不在这里。您是否注意到,在我强制执行 2 次(实际上是 4 次,但只有前 2 次重要)垃圾回收后,内存使用量又回到了“正常状态”(与首次加载阶段时的内存使用量相同)?这就是我说的奇怪现象.

问题

If the 垃圾收集器应该从内存中删除不再被引用的对象(或者至少只有弱引用), 为什么你上面看到的“垃圾内存”只有当我强行调用时才会被删除GC并且不在GC's正常处决?我的意思是,如果垃圾收集我手动启动可以消除这个“thrash”,然后正常GC's处决也能够将其删除。为什么它没有发生?

我什至尝试过打电话系统.gc()当阶段被切换时,但是,即使垃圾收集发生这种情况时,这个“thrash”内存不会像我手动执行时那样被删除GC。我是否遗漏了一些关于如何垃圾收集器工作原理或者 Android 如何实现它?

最后的考虑因素

我花了几天时间搜索、研究和修改我的代码,但我无法找出为什么会发生这种情况。 StackOverflow 是我最后的手段。谢谢你!

NOTE:我本来打算发布我的应用程序源代码的一些可能相关的部分,但由于问题已经太长了,我将在此停止。如果您觉得需要检查一些代码,请告诉我,我将编辑这个问题。

我已经读过的内容:
Java中如何强制垃圾回收? https://stackoverflow.com/questions/1481178/how-to-force-garbage-collection-in-java
Android 中的垃圾收集器 https://stackoverflow.com/questions/3117429/garbage-collector-in-android
Oracle 的 Java 垃圾收集基础知识 https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html
Android 内存概述 https://developer.android.com/topic/performance/memory-overview
Android 中的内存泄漏模式 https://android.jlelse.eu/memory-leak-patterns-in-android-4741a7fcb570
避免 Android 中的内存泄漏 https://android-developers.googleblog.com/2009/01/avoiding-memory-leaks.html
管理应用程序的内存 https://developer.android.com/topic/performance/memory
关于 Android 应用程序内存泄漏您需要了解的内容 https://techbeacon.com/what-you-need-know-about-android-app-memory-leaks
使用 Memory Profiler 查看 Java 堆和内存分配 https://developer.android.com/studio/profile/memory-profiler
LeakCanary(适用于 Android 和 Java 的内存泄漏检测库) https://github.com/square/leakcanary
Android 内存泄漏和垃圾收集 https://stackoverflow.com/questions/31635184/android-memory-leak-and-garbage-collection
通用 Android 垃圾收集 https://stackoverflow.com/questions/11035593/generic-android-garbage-collection
如何从内存中清除动态创建的视图? https://stackoverflow.com/questions/9949047/how-to-clear-dynamically-created-view-from-memory
引用在 Android 和 Java 中如何工作 https://medium.com/google-developer-experts/finally-understanding-how-references-work-in-android-and-java-26a0d9c92f83
Java 垃圾收集器 - 未定期正常运行 https://stackoverflow.com/questions/4309105/java-garbage-collector-not-running-normally-at-regular-intervals
android 中的垃圾收集(手动完成) https://stackoverflow.com/questions/8177802/garbage-collection-in-android-done-manually
...还有更多我找不到了。


垃圾收集很复杂,不同平台的实现也不同。确实不同versions同一平台的垃圾回收实现方式不同。 (和更多 ... )

典型的现代收藏家基于以下观察:most物体在年轻时就会死亡;即它们在创建后很快就变得无法访问。然后堆被分成两个或多个“空间”;例如一个“年轻”的空间和一个“老”的空间。

  • “年轻”空间是新物品被创造的地方,并且被频繁地收集。 “年轻”的空间往往较小,而“年轻”的收藏发生得很快。
  • “旧”空间是长期存在的对象最终存放的地方,并且很少被收集。在“旧”空间收集往往更昂贵。 (由于各种原因。)
  • 在“新”空间中存活下来的多个 GC 周期的对象将获得“终身”;即它们被移动到“旧”空间。
  • 有时我们可能会发现需要同时收集新旧空间。这称为完整集合。完整的 GC 是最昂贵的,并且通常会“停止世界”相对较长的时间。

(还有各种各样其他聪明而复杂的事情......我不会详细讨论。)


您的问题是为什么空间使用量在您致电之前没有显着下降System.gc().

答案基本上是这样的高效的做事的方式。

收集的真正目标不是始终释放尽可能多的内存。相反,目标是确保在需要时有足够的可用内存,并以最小的 CPU 开销或最小的 GC 暂停来实现这一点。

因此,在正常操作中,GC 的行为将如上所示:进行频繁的“新”空间收集和不太频繁的“旧”空间收集。还有收藏品 将“按要求”运行。

但当你打电话时System.gc()JVM 将通常尝试恢复尽可能多的内存。这意味着它会执行“完整GC”。

现在我想你说过这需要几个System.gc()呼吁做出真正的改变,这可能与使用finalize方法或Reference物体或类似物体。事实证明,可终结的对象和Reference主 GC 完成后由后台线程进行处理。对象实际上仅处于可以收集和删除的状态after那。所以需要再次GC来最终摆脱它们。

最后,还有总堆大小的问题。当堆太小时,大多数虚拟机会向主机操作系统请求内存,但又不愿意归还内存。 Oracle 收集器会记录连续“完整”收集结束时的可用空间比率。仅当经过多次 GC 循环后可用空间比率“太高”时,它们才会减小堆的总体大小。 Oracle GC 采取这种方法有多种原因:

  1. 当垃圾与非垃圾对象的比例较高时,典型的现代 GC 工作效率最高。因此保持堆很大有助于提高效率。

  2. 应用程序的内存需求很可能会再次增长。但 GC 需要运行来检测这一点。

  3. JVM 反复将内存返还给操作系统并重新请求它可能会破坏操作系统虚拟内存算法。

  4. 如果操作系统内存资源不足就会出现问题;例如JVM:“我不需要这个内存。把它拿回来”,操作系统:“谢谢”,JVM:“哦......我再次需要它!”,操作系统:“不”,JVM:“OOME”。

假设 Android 收集器以同样的方式工作,这就是为什么你必须运行的另一种解释System.gc()多次以缩小堆大小。


在开始添加之前System.gc()调用您的代码,阅读为什么调用 System.gc() 是不好的做法? https://stackoverflow.com/questions/2414105/why-is-it-bad-practice-to-call-system-gc.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

垃圾收集器没有像 Android 应用程序中那样释放“垃圾内存” 的相关文章

  • Install4j:如何在安装结束时执行命令行 java -jar filename.jar

    在 Intall4j 中 在安装结束时 我只想通过执行如下命令行来初始化某些内容 java jar filename jar 我怎样才能归档这个任务install4j Thanks 将 运行可执行文件或批处理文件 操作添加到 安装屏幕 并设
  • 覆盖 MATLAB 默认静态 javaclasspath 的最佳方法

    MATLAB 配置为在搜索用户可修改的动态路径之前搜索其静态 java 类路径 不幸的是 静态路径包含相当多非常旧的公共库 因此如果您尝试使用新版本 您可能最终会加载错误的实现并出现错误 例如 静态路径包含 google collectio
  • 手动启用时 Firebase Crashlytics 不报告崩溃

    Crashlytics 在没有选择加入报告的情况下也能正常工作 但一旦我根据规定设置了选择加入报告 它就会停止报告任何内容tutorial https firebase google com docs crashlytics customi
  • tomcat 过滤所有 web 应用程序

    问题 我想对所有网络应用程序进行过滤 我创建了一个过滤器来监视对 apache tomcat 服务器的请求 举例来说 它称为 MyFilter 我在 netbeans 中创建了它 它创建了 2 个独立的目录 webpages contain
  • Android:如何监控WiFi信号强度

    当信号强度发生变化时我会收到通知 我尝试创建以下方法并在 onCreate 中调用它 private void initializeWiFiListener Log i TAG executing initializeWiFiListene
  • Apache Commons CLI:替代已弃用的 OptionBuilder?

    IntelliJ 显示此示例代码中不推荐使用 OptionBuilderhttp commons apache org proper commons cli usage html http commons apache org proper
  • 在 AKKA 中,对主管调用 shutdown 是否会停止其监督的所有参与者?

    假设我有一位主管连接了 2 位演员 当我的应用程序关闭时 我想优雅地关闭这些参与者 调用supervisor shutdown 是否会停止所有参与者 还是我仍然需要手动停止我的参与者 gracias 阻止主管 https github co
  • 我必须做什么才能使通过 HTTPS 提供的图像等内容缓存在客户端?

    我使用 Tomcat 作为服务器 使用 Internet Explorer 6 作为浏览器 我们应用程序中的网页大约有 75 张图像 我们正在使用 SSL 加载所有内容似乎非常慢 如何配置 Tomcat 以便 IE 缓存图像 如果您通过 h
  • OpenJDK 版本控制

    上下文 我想确保我们系统上安装的 Java 不受 CVE 2022 21449 的影响 java version 给出 openjdk version 11 0 7 2020 04 14 LTS OpenJDK Runtime Enviro
  • 如何为 Jackson 编写一个包罗万象的(反)序列化器

    当您提前知道类型时 编写自定义序列化器非常容易 例如 MyType一个人可以写一个MyTypeSerializer extends StdSerializer
  • 在 Selenium WebDriver 上如何从 Span 标签获取文本

    在 Selenium Webdriver 上 如何从 span 标记检索文本并打印 我需要提取文本UPS Overnight Free HTML代码如下 div id customSelect 3 class select wrapper
  • ExceptionHandler 不适用于 Throwable

    我们的应用程序是基于 Spring MVC 的 REST 应用程序 我正在尝试使用 ExceptionHandler 注释来处理所有错误和异常 I have ExceptionHandler Throwable class public R
  • Spock模拟inputStream导致无限循环

    我有一个代码 gridFSFile inputStream bytes 当我尝试这样测试时 given def inputStream Mock InputStream def gridFSDBFile Mock GridFSDBFile
  • Path2D 上的鼠标指针检测

    我构建了一个Path2D http docs oracle com javase 7 docs api java awt geom Path2D html表示由直线组成的未闭合形状 我希望能够检测何时单击鼠标并且鼠标指针靠近路径 在几个像素
  • Jetpack Compose 部分或开放侧边框

    我正在尝试绘制部分或一侧开放的矩形圆形边框以实现此效果 玩了一下之后我得到了这个 这是通过以下方式完成的 RoundedCornerShape topStartPercent 50 bottomStartPercent 50 start R
  • OpenGL ES 2.0 屏幕闪烁

    我面临着一个大问题 我正在使用带有 Android 4 0 3 的 Transformer tf101 选项卡 我的应用程序使用自定义 OpenGL ES 2 0 表面 我正在用纹理渲染多个平面 该纹理大约发生变化 每秒 20 次 并通过传
  • Android应用程序kill事件捕获

    我想在我的应用程序被终止时执行一些操作 可以使用哪种方法来实现此目的 我正在开发 Android 5 0 这个问题的关键在于 您必须了解您的申请是否可以收到任何 当您的应用程序在任何情况下被终止时的额外回调 下面的答案是由德文连线 http
  • Android:获取最新意图

    如何获取发送到活动的最后一个意图 的文档onNewIntent 建议我需要做这样的事情 class MyActivity public void onNewIntent Intent intent setIntent intent reac
  • 在 Android 中更新到 API 26 时,清单合并失败并出现多个错误

    我尝试使用 API 26 更新我的 gradle 安卓工作室2 3 3 但我在编译项目时遇到以下错误 这是我收到的错误的屏幕截图 应用级别build gradle Top level build file where you can add
  • 为什么这个函数在额外读取内存时运行速度如此之快?

    我目前正在尝试了解 x86 64 上某些循环的性能属性 特别是我的 Intel R Core TM i3 8145U CPU 2 10GHz 处理器 具体来说 在循环体内添加一条额外的指令来读取内存几乎可以使性能提高一倍 而细节并不是特别重

随机推荐