代码之家  ›  专栏  ›  技术社区  ›  bwawok

减少内存流失的方法

  •  10
  • bwawok  · 技术社区  · 14 年前

    背景

    我的程序使用1个线程读取文件,12个工作线程执行处理和数据库推送。

    我在翻腾很多很多年轻一代的内存,这导致我的程序比我想象的要慢。

    JDK 1.6.18版本


    -Xmx12G 
    -Xms12G 
    -NewRatio=1 
    -XX:+UseParallelGC
    -XX:+UseParallelOldGC
    

    问题

    然而,年轻一代却屡屡点击它的最大值。它会上升到5gb的范围,然后发生一个并行的小GC,并将younggen清除到500MB。小GC是好的,比一个完整的GC更好,但它仍然会减慢我的程序很多(我很肯定,应用程序仍然冻结时,年轻一代的收集发生,因为我看到数据库活动消失)。我花了超过5%的时间冻结我的计划为次要GCs,这似乎是过分的。我会说 在处理这个4GB文件的过程中,我用了50-60GB的young gen内存

    我的程序没有明显的缺陷。我试图遵循一般的OO原则,编写干净的Java代码。我试着不要无缘无故地创建对象。我正在使用线程池,只要有可能就传递对象,而不是创建新对象。我将开始分析应用程序,但是 我想知道是否有人有一些好的经验法则或反模式,以避免导致过度的内存流失 ? 我能做的最好的方法是使用50-60GB的内存来处理4GB文件吗?我是否必须恢复到JDK1.2技巧,比如对象池(尽管Brian Goetz做了一个演示,包括为什么对象池是愚蠢的,我们不需要再做了。我比我自己更信任他……)

    7 回复  |  直到 14 年前
        1
  •  2
  •   Peter Tillemans    14 年前

    我认为与记忆档案器的一个会议将对这个问题有很大的启发。这提供了一个很好的概述有多少对象被创建,这是一些启示。

    我总是很惊讶有多少字符串被生成。

    对于域对象,交叉引用它们也很有意义。如果你突然看到一个派生对象中的对象比源对象中的多3倍,那么那里就发生了一些事情。

    Netbeans有一个很好的构建了它。我以前用过JProfiler。我认为如果你在eclipse上花足够长的时间,你可以从PPTP工具得到同样的信息。

        2
  •  9
  •   Stephen C    12 年前

    我有一种感觉,你正在花费时间和精力试图优化一些你不应该费心的事情。

    把它翻过来。你花了将近95%的时间做有用的工作。或者换一种方式说,即使您成功地优化了GC以零时间运行,您所能得到的最好的结果是超过5%的改进。

    如果应用程序具有受暂停时间影响的硬计时要求,则可以考虑使用低暂停收集器(注意减少暂停时间 增加 总GC开销…)但是对于批处理作业,GC暂停时间应该不相关。

    最重要的可能是整个批处理作业的挂钟时间。而且(大约)95%的时间花在做特定于应用程序的事情上,你很可能会从你的评测/目标优化工作中得到更多的回报。例如,您是否查看了成批发送到数据库的更新?


    所以。。我总内存的90%在“oracle.sql.converter.toOracleStringWithReplacement”中的char[]中

    这可能表明,在准备将内容发送到数据库时,大部分内存使用都发生在oraclejdbc驱动程序中。关于那件事你几乎没有什么意见。我认为这是不可避免的开销。

        3
  •  3
  •   Tobias P.    14 年前

    你试过不同的垃圾收集算法吗?“UseConcMarkSweepGC”或“UseParNewGC”是如何执行的。

    不要忘记简单地增加可用空间不是解决方案,因为gc运行需要更长的时间,将大小减小到正常值;)

    你确定没有内存泄漏吗?在消费者-生产者模式中——您描述过——很少有数据应该在旧的Gen中,因为这些工作处理得非常快,然后被“扔掉”,或者您的工作队列已经满了?

    你应该用内存分析器来观察你的程序。

        4
  •  2
  •   Pascal Thivent    14 年前

    您需要分析您的应用程序,以查看到底发生了什么。我也会先尝试使用 人体工程学 JVM的特性,如推荐的:

    2. Ergonomics

    j2se5.0引入了人机工程学。 人机工程学的目标是提供 性能好,很少或没有 命令行选项的调整 选择

    • 垃圾收集器,
    • 和运行时编译器

    默认值。此选择假定 在其上运行 应用特点 机器)。除了这些 选择是一种简化的 调整垃圾回收。与 并行收集器用户可以 指定最大暂停时间的目标 应用程序。这与 指定生成的堆的大小 旨在特别改善 使用大堆的。更一般的 人体工程学在 5.0 Java虚拟机。 建议将人机工程学 在后一份文件中提出 本文档中说明的控件 .

    本文件包括 的自适应大小策略 平行收集器。这包括 用于指定目标的选项 垃圾收集和处理性能 其他微调选项 性能。

    请参阅有关 Ergonomics Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning

        5
  •  1
  •   Roland Illig    14 年前

    在我看来,年轻一代不应该和老一代一样大,这样小垃圾收集就留得快。

    是否有许多对象表示相同的值?如果这样做,请使用简单的 HashMap :

    public class MemorySavingUtils {
    
        ConcurrentHashMap<String, String> knownStrings = new ConcurrentHashMap<String, String>();
    
        public String unique(String s) {
            return knownStrings.putIfAbsent(s, s);
        }
    
        public void clear() {
            knownStrings.clear();
        }
    }
    

    使用Sun热点编译器,本机 String.intern()

        6
  •  1
  •   irreputable    14 年前

    从文件中读取一行,存储为字符串并放入列表。当列表中有1000个这样的字符串时,将其放入工作线程要读取的队列中。工作线程创建一个域对象,从字符串中剥离一堆值来设置字段(int、long、java.util.Date或string),并将域对象传递给默认的spring批处理jdbc编写器

    如果这是你的程序,为什么不设置一个较小的内存大小,如256MB?

        7
  •  1
  •   Ceilingfish    14 年前

    我猜在内存限制这么高的情况下,在进行处理之前,您必须将文件完全读入内存。你能考虑使用 java.io.RandomAccessFile 相反呢?