【问题标题】:Is it worth mitigating against the effects of garbage collection?是否值得减轻垃圾收集的影响?
【发布时间】:2010-11-16 23:23:57
【问题描述】:

我有一个应用程序,其内存配置文件如下所示:


(来源:kupio.com

内存使用量缓慢向上爬行是由于分配了大量的小、简单、临时对象造成的。在内存不足的情况下(这是一个移动应用程序),与限制较少的内存量相比,GC 开销很明显。

由于我们知道,由于应用程序的性质,这些峰值会不断出现,因此我正在考虑某种类型的大量瞬态对象池(真棒名称)。这些对象将在应用程序的整个生命周期中存在,并尽可能重复使用(对象的生命周期很短且高度可预测)。

希望这将通过减少收集的对象数量并提高性能来减轻 GC 的影响。

显然,这也会有其自身的性能限制,因为“分配”会更昂贵,并且维护缓存本身会产生开销。

由于这将是对大量代码的相当大且侵入性的更改,我想知道是否有人尝试过类似的东西,是否有好处,或者是否有任何其他已知的方法来缓解 GC种情况。也欢迎有关管理可重用对象缓存的有效方法的想法。

【问题讨论】:

  • “大量潜在对象的大型缓存” == 池。
  • pool == "没那么有趣" :P
  • 知道你正在暂停吗?
  • pool == "标准术语"。
  • 是的,有 GC 暂停。应用程序允许的内存越少,应用程序的速度就越慢。有了更多内存,应用程序感觉更快,但当它对用户不太重要时(基本上是在适当的时间调用 System.gc() 时)倾向于 GC。

标签: java memory-management caching java-me garbage-collection


【解决方案1】:

这类似于 GoF 模式手册中详述的 flyweight 模式(请参阅下面的编辑)。由于在减少对象创建、同步和 GC 开销方面取得的进步,对象池在“普通”虚拟机中已经失宠。但是,这些肯定已经存在很长时间了,尝试它们看看它们是否有帮助当然没问题!

当然 对象池 仍然用于与上述池开销相比具有非常昂贵的创建开销的对象(数据库连接 是一个明显的例子)。

只有测试才能告诉您池化方法是否适用于您的目标平台!

编辑 - 我将 OP “尽可能重复使用” 表示对象是不可变的。当然,情况可能并非如此,享元模式实际上是关于共享不可变对象(Enums 是享元的一个示例)。可变(读作:不可共享)对象不是享元模式的候选对象,但(当然)是对象池的候选对象。

【讨论】:

  • 这没有回答问题:OP应该这样做
  • 我认为答案有点含蓄。如果 GoF 写了一本书,将其作为一种常见模式包含在内,那么它就是该问题的潜在解决方案
【解决方案2】:

通常,我会说这是一项用于调整 VM 的 GC 参数、减少尖峰性的工作,但对于移动应用程序来说,这并不是一个真正的选择。因此,如果您使用的 JVM 无法修改其 GC 行为,那么老式的对象池可能是最好的解决方案。

Apache Commons Pool 库对此很有帮助,但如果这是一个移动应用程序,那么您可能不希望库依赖开销。

【讨论】:

    【解决方案3】:

    实际上,这张图表对我来说看起来很健康。 GC 正在回收大量对象,然后内存返回到相同的基本级别。根据经验,这意味着 GC 正在有效地工作。

    对象池的问题在于它会使您的应用程序变得更慢、更复杂并且可能存在更多错误。更重要的是,它实际上可以使每次 GC 运行时间更长。 (池中的所有“空闲”对象都是非垃圾,需要被 GC 标记等。)

    【讨论】:

      【解决方案4】:

      J2ME 有分代垃圾收集器吗?如果是这样,它会进行许多小而快速的收集,从而减少暂停。您可以尝试减少 eden 内存空间(小内存空间)以增加频率并减少收集的延迟,从而减少暂停。

      不过,仔细想想,我的猜测是你无法调整 gc 行为,因为一切都可能在同一个 VM 中运行(这里只是猜测)。

      【讨论】:

        【解决方案5】:

        您可以查看 this link 描述并发标记扫描收集器的增强功能,尽管我不确定它是否可用于 J2ME。特别注意:

        “并发标记清除收集器,也称为并发收集器或 CMS,针对的是对垃圾收集暂停敏感的应用程序。”

        ...“在 JDK 6 中,CMS 收集器可以选择同时执行这些收集,以避免响应 System.gc() 或 Runtime.getRuntime().gc() 调用而长时间暂停。启用此功能功能,添加选项”

        -XX:+ExplicitGCInvokesConcurrent 
        

        【讨论】:

          【解决方案6】:

          查看this link。特别是:

          只是列出一些问题 对象池创建:首先,一个未使用的 对象不占用内存空间 原因; GC 必须处理未使用的 物品也一样,将其扣留 无缘无故的无用物品;并且在 为了从 对象池同步是 通常需要这要慢得多 比异步分配 本机可用。

          【讨论】:

            【解决方案7】:

            您说的是可重用对象实例池。

            class MyObjectPool { 
                List<MyObject> free= new LinkedList<MyObject>();
                List<MyObject> inuse= new LinkedList<MyObject>();
                public MyObjectPool(int poolsize) {
                    for( int i= 0; i != poolsize; ++i ) {
                       MyObject obj= new MyObject();
                       free.add( obj );
                    }
                }
                pubic makeNewObject( ) {
                    if( free.size() == 0 ) {
                        MyObject obj= new MyObject();
                        free.add( obj );
                    }
                    MyObject next= free.remove(0);
                    inuse.add( next );
                    return next;
               }
               public freeObject( MyObject obj ) {
                   inuse.remove( obj );
                   free.add( obj );
               }
            }
                    return in
            

            【讨论】:

              【解决方案8】:

              鉴于this 的回答表明在 J2ME 中调整垃圾收集本身的空间不大,那么如果 GC 是一个问题,那么唯一的另一个选择是查看如何更改应用程序以提高性能/内存使用率。也许引用的答案中的一些建议适用于您的应用程序。

              正如 oxbow_lakes 所说,您建议的是标准设计模式。但是,与任何优化一样,真正了解它将在多大程度上改进您的特定应用程序的唯一方法是实施和分析。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-03-11
                • 1970-01-01
                • 1970-01-01
                • 2011-01-26
                相关资源
                最近更新 更多