Java 减少内存流失的方法 背景

Java 减少内存流失的方法 背景,java,memory-management,garbage-collection,spring-batch,Java,Memory Management,Garbage Collection,Spring Batch,我有一个Spring批处理程序,它读取一个文件(我正在处理的示例文件大小约为4 GB),对该文件进行少量处理,然后将其写入Oracle数据库 我的程序使用1个线程读取文件,12个工作线程执行处理和数据库推送 我正在翻腾很多很多年轻一代的内存,这导致我的程序比我想象的要慢 安装程序 JDK 1.6.18 弹簧批2.1.x 带16 GB ram的4芯机器 -Xmx12G -Xms12G -NewRatio=1 -XX:+UseParallelGC -XX:+UseParallelOldGC

我有一个Spring批处理程序,它读取一个文件(我正在处理的示例文件大小约为4 GB),对该文件进行少量处理,然后将其写入Oracle数据库

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

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

安装程序 JDK 1.6.18
弹簧批2.1.x
带16 GB ram的4芯机器

-Xmx12G 
-Xms12G 
-NewRatio=1 
-XX:+UseParallelGC
-XX:+UseParallelOldGC
问题 有了这些JVM参数,我得到了大约5.xGB的内存供终身使用,而大约5.xGB的内存供年轻一代使用

在处理这一个文件的过程中,我这一代人很好。它的最大容量可能会增加到3GB,而且我永远不需要做一次完整的GC

然而,年轻一代却多次打出了这首歌。它可以达到5GB的范围,然后发生并行的次要GC,并将Young Gen清除到所使用的500MB。小GC很好,比完整GC好,但它仍然会让我的程序慢很多(我很确定当年轻一代收集时,应用程序仍然会冻结,因为我看到数据库活动消失)。我花了超过5%的课程时间冻结在次要的GCs上,这似乎太多了。我想说在处理这个4GB文件的过程中,我大量使用了50-60GB的young gen内存


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

我认为与内存分析器的对话会对这个问题有很大的帮助。这很好地概括了创建了多少对象,这很有启发性

我总是惊讶于生成了多少字符串

对于域对象,交叉引用它们也很有启发性。如果从衍生对象中突然看到的对象比从源对象中看到的对象多3倍,则会发生一些事情


Netbeans有一个很好的构建它的工具。我过去用过JProfiler。我认为,如果你在eclipse上玩的时间足够长,你可以从PPTP工具中获得相同的信息。

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

是否有许多对象表示相同的值?如果这样做,请使用简单的
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();
    }
}
公共类MemorySavingUtils{
ConcurrentHashMap knownStrings=新的ConcurrentHashMap();
公共字符串唯一(字符串s){
返回knownStrings.putIfAbsent(s,s);
}
公共空间清除(){
knownStrings.clear();
}
}
在Sun Hotspot编译器中,本机
String.intern()
对于大量字符串来说速度非常慢,这就是为什么我建议构建自己的字符串interner


使用这种方法,旧一代的字符串可以被重用,新一代的字符串可以被快速垃圾收集。

如果您澄清术语“年轻的”和“有希望的”一代,这将非常有用,因为Java 6有一个稍微不同的GC模型:Eden、S0+S1、old、Perm

您是否尝试过不同的垃圾收集算法?“UseConMarkSweepGC”或“UseParNewGC”是如何执行的

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

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

您应该使用内存分析器明确地观察您的程序

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


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

我猜内存限制这么高,您必须在处理之前将文件完全读入内存。你能考虑使用A代替吗?

< P>你需要对你的应用程序进行剖析,看看到底发生了什么。我还将首先尝试使用JVM的人体工程学特性,正如推荐的那样:

此处称为 J2SE5.0引入了人机工程学。 人体工程学的目标是提供 性能良好,很少或没有 通过 选择

  • 垃圾收集器
  • 堆大小
  • 和运行时编译器
在JVM启动时,而不是使用固定的 默认值。此选项假定 在其上执行操作的机器的类别 应用程序运行是关于 应用程序的特点 (即,大型应用程序在大型计算机上运行 机器)。除此之外, 选择是一种简化的方法 调整垃圾收集。和 并行收集器用户可以 指定最大暂停时间的目标 还有一个