是否值得减轻垃圾收集的影响?

问题描述 投票:11回答:8

我有一个应用程序,内存配置文件看起来像这样:

Jaggy (来源:kupio.com

内存使用的缓慢向上爬行是由批次和大量小而简单的瞬态对象的分配引起的。在内存不足的情况下(这是一个移动应用程序),与较少限制的内存量相比,GC开销很明显。

因为我们知道,由于应用程序的性质,这些尖峰将继续存在,我正在考虑某种众多的瞬态对象池(真棒名称)。这些对象将在应用程序的生命周期中存在,并在可能的情况下重新使用(对象的生命周期很短且可预测性很强)。

希望这可以减少收集的对象数量并提高性能,从而减轻GC的影响。

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

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

java memory-management caching java-me garbage-collection
8个回答
2
投票

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

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


6
投票

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

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

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

编辑 - 我把OP“尽可能重用”意味着对象是不可变的。当然可能不是这种情况,而且flyweight模式实际上是关于共享的不可变对象(Enums是flyweight的一个例子)。可变(read:unshareable)对象不是flyweight模式的候选对象,而是(当然)对象池的候选对象。


2
投票

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

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


2
投票

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

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


1
投票

你可以查看this link描述Concurrent Mark Sweep收集器的增强功能,虽然我不确定它是否适用于J2ME。特别注意:

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

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

-XX:+ExplicitGCInvokesConcurrent 

1
投票

看看this link。特别是:

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


0
投票

您正在谈论可重用对象实例池。

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

0
投票

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

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

© www.soinside.com 2019 - 2024. All rights reserved.