【问题标题】:Java ParNew Collection moving objects to Old GenerationJava ParNew Collection 将对象移动到老年代
【发布时间】:2013-12-01 17:06:51
【问题描述】:

这是我的虚拟机参数:

-XX:MaxPermSize=128m 
-XX:+UseParNewGC 
-XX:MaxNewSize=1G 
-XX:NewSize=1G 
-Xms13G 
-Xmx13G 
-XX:SurvivorRatio=128 
-XX:MaxTenuringThreshold=0 
-XX:+UseTLAB 
-XX:+UseConcMarkSweepGC 
-verbose:gc 
-XX:+PrintGCDetails 
-XX:+PrintGCTimeStamps 
-Xloggc:./gc.log -cp

我还造成了内存泄漏:

{
    String s = "";
    for (int i = 0; i < objectsCount; i++)
        s += "s" + s;
}//Here all created objects should be garbage collected

所以我已经运行了这部分代码几次,我注意到大部分对象被移动到老年代,只有一些被小型 GC 收集

832.135: [GC 832.135: [ParNew: 1032095K->0K(1040512K), 0.7309045 secs] 4832264K->4761739K(13623424K), 0.7309703 secs] [Times: user=2.37 sys=0.33, real=0.73 secs] 
833.148: [GC 833.148: [ParNew: 826257K->0K(1040512K), 0.1510836 secs] 5587996K->5095138K(13623424K), 0.1511436 secs] [Times: user=0.33 sys=0.00, real=0.15 secs] 
833.567: [GC 833.567: [ParNew: 742459K->0K(1040512K), 0.2067575 secs] 5837597K->5836296K(13623424K), 0.2068291 secs] [Times: user=0.75 sys=0.00, real=0.21 secs] 
853.490: [GC 853.582: [ParNew: 993475K->0K(1040512K), 0.6963577 secs] 8100247K->7107608K(13623424K), 0.6968597 secs] [Times: user=1.61 sys=0.00, real=0.79 secs] 
879.973: [GC 879.973: [ParNew: 1032433K->0K(1040512K), 10.6403908 secs] 8140042K->8061725K(13623424K), 10.6404844 secs] [Times: user=30.93 sys=0.34, real=10.64 secs] 

我认为原因是,太多的对象同时粘贴到内存中,所以垃圾收集器更方便地将它们从年轻代或老年代移出。

我的问题是,我应该改变我的论点,以使所有这些对象都被 Small GC 清除?

【问题讨论】:

    标签: java object garbage-collection


    【解决方案1】:

    将字符串 s = "" 移动到循环中,然后尝试。

    这里的事情是,直到 for 循环完成,变量 s 才被标记为 GC。您看到的 GC 日志条目是在 for 循环进行时。因为程序用完了年轻代空间,而 for 循环仍在运行,GC 收集器将 S 的值移动到老代,因为它不知道还能做什么。


    而不是这样做

    String s = "";
    for(int i = 0; i < counter; i++){
        s = s + "something";
    }
    

    这样做

    for(int i = 0; i < counter; i++){
        String s = "something";
        //call some methods to process it.
    }
    

    或使用 StringBuilder 来创建字符串连接,而不是在字符串上使用 + 运算符。


    -d64 -server -Xms4096m -Xmx4096m -XX:PermSize=512m -XX:MaxPermSize=512m -XX:NewSize=1536m -XX:MaxNewSize=1536m -XX:-UseAdaptiveSizePolicy -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseTLAB -XX:+UseConcMarkSweepGC -XX:+OptimizeStringConcat -XX:ParallelGCThreads=14 -XX:ConcGCThreads=14
    

    根据您的需要调整烫发尺寸。 NewSize 是你的年轻一代,剩下的堆分配给老一代。试试这个,看看它是否对你有帮助。

    【讨论】:

    • “因为程序用完了年轻代空间,而 for 循环仍在运行,GC 收集器将 S 的值移动到老代,因为它不知道还能做什么。” - 是的,有什么我能做的吗?因为在我的程序中所有像这样的对象都将被标记为长期变量,它们需要等待 Collection 来查看 Old Generation。
    • 这就是我在回答中所说的。在循环内部而不是外部创建变量。检查我的答案中的编辑
    • for (int i = 0; i
    • 是什么让你相信它会被转移到老年代? b 仅对 for 循环的单次迭代有效。所以当迭代完成时 b 完成。除非您在迭代完成之前泄漏对 b 的引用,否则它将有资格进行 GC。顺便说一句,年轻一代的魅力是什么?如果它确实在您现有的代码中进入老年代,那么麻烦是什么? odl gen 将在主要 GC 周期期间进行 GC,这将恢复内存。
    • “因为程序用完了年轻代空间,而 for 循环仍在运行,GC 收集器将 S 的值移动到老代,因为它不知道还能做什么。” - 在这里你告诉我,如果 b 对象当前被使用(b.append("asd")) 那么 GC 将 b 移动到旧代。 Major GC 停止 VM 时间过长
    【解决方案2】:

    让我为你解释一下你的 JVM 参数。

    -XX:NewSize=1G - 这是年轻空间的大小(用“小”集合收集的空间)。

    -XX:SurvivorRatio=128 - 这会将年轻空间分割为约 1008 MiB 的 Enden + 2 个幸存者空间,每个空间 8 MiB。年轻(“小”)收集发生在伊甸园被填满时。

    -XX:MaxTenuringThreshold=0 - 此参数强制 JVM 立即将在一次年轻 GC 中幸存的对象提升到旧空间(因此从不使用幸存者空间)。通常,对象应该在被提升到旧空间之前经历几次“小型”GC,但您已明确禁用它。

    Here你可以在HotSpot JVM中找到更多关于分代收集的细节。

    Cheatsheet of HotSpot GC options 也可能有用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-06-02
      • 1970-01-01
      • 2012-07-28
      • 1970-01-01
      • 2021-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多