使用最少GC的方法将double转换为没有E和10位精度的String

时间:2016-07-28 15:31:06

标签: java apache-spark garbage-collection

我的火花工作正在创建许多小对象并遇到垃圾收集问题。我已经尝试了多种方法将double转换为具有10位精度的String而没有Scientific符号。

第一种方式

String.format("%.10f", denomValue)

此案例的错误:

java.lang.OutOfMemoryError: GC overhead limit exceeded
at sun.misc.FormattedFloatingDecimal.create(FormattedFloatingDecimal.java:254)
at sun.misc.FormattedFloatingDecimal.fillDecimal(FormattedFloatingDecimal.java:267)
at sun.misc.FormattedFloatingDecimal.<init>(FormattedFloatingDecimal.java:76)
at sun.misc.FormattedFloatingDecimal.valueOf(FormattedFloatingDecimal.java:38)
at java.util.Formatter$FormatSpecifier.print(Formatter.java:3298)
at java.util.Formatter$FormatSpecifier.print(Formatter.java:3238)
at java.util.Formatter$FormatSpecifier.printFloat(Formatter.java:2802)
at java.util.Formatter$FormatSpecifier.print(Formatter.java:2753)
at java.util.Formatter.format(Formatter.java:2520)
at java.util.Formatter.format(Formatter.java:2455)
at java.lang.String.format(String.java:2925)

第二种方式

new BigDecimal(d).setScale(10,BigDecimal.ROUND_UP).toPlainString()

这种情况的错误: -

java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.math.MutableBigInteger.divideMagnitude(MutableBigInteger.java:1489)
at java.math.MutableBigInteger.divideKnuth(MutableBigInteger.java:1227)
at java.math.MutableBigInteger.divide(MutableBigInteger.java:1153)
at java.math.MutableBigInteger.divide(MutableBigInteger.java:1147)
at java.math.BigDecimal.divideAndRound(BigDecimal.java:4326)
at java.math.BigDecimal.setScale(BigDecimal.java:2470)

2 个答案:

答案 0 :(得分:1)

引用&#34; Understand the OutOfMemoryError Exception&#34;:

  

thread thread_name中的异常:java.lang.OutOfMemoryError:超出GC开销限制

     

原因:详细消息&#34; GC开销限制超出&#34;表示垃圾收集器一直在运行,Java程序进展非常缓慢。在垃圾收集之后,如果Java进程花费超过大约98%的时间进行垃圾收集,并且它正在恢复少于2%的堆并且到目前为止已经执行了最后5个(编译时常量)连续垃圾集合,然后抛出java.lang.OutOfMemoryError。通常会抛出此异常,因为实时数据量几乎不适合Java堆,新分配的可用空间很小。

     

操作:增加堆大小。可以使用命令行标志-XX:-UseGCOverheadLimit关闭超出GC Overhead限制的java.lang.OutOfMemoryError异常。

如果您的代码真正花费98%的时间进行垃圾收集,那么您肯定应该增加堆大小,因为这意味着98%的内存用于(半)永久数据,少于2 %可以免费进行临时分配。

您可能遇到严重的内存泄漏问题。

但正如它所说,如果需要,您可以禁用该特定错误。

至于你的代码,你应该坚持使用选项1.选项2将创建更多垃圾选项1。

答案 1 :(得分:0)

我已经对少数意见进行了基准测试以格式化十进制数

  • 的java.util.Formatter
  • java.lang.String.format()
  • java.text.DecimalFormat中
  • org.apache.xmlgraphics.util.DoubleFormatUtil.formatDouble()

在所有情况下,我都测量了内存浪费,以格式化一个十进制数。 以下是32 JVM的结果。

java.util.Formatter
Format mem usage: 1120
Format + string mem usage: 1184

java.text.DecimalFormat
Format + string mem usage: 288

org.apache.xmlgraphics.util.DoubleFormatUtil.formatDouble()
Format mem usage: 0
Format + string mem usage: 64

java.lang.String.format()
Format + string mem usage: 1448

代码低于

@Test
public void testDecimalFormat() {
    double pi = Math.PI;
    System.out.println("\njava.text.DecimalFormat");

    DecimalFormat fmt = new DecimalFormat(".##########");
    fmt.setRoundingMode(RoundingMode.UP);

    // warm up
    fmt.format(pi);

    long mme = -(getCurrentThreadAllocatedBytes() - getCurrentThreadAllocatedBytes());
    long memu1 = getCurrentThreadAllocatedBytes();

    String val = fmt.format(pi);

    long memu2 = getCurrentThreadAllocatedBytes();

    System.out.println(val);
    System.out.println("Format + string mem usage: " + (memu2 - memu1 - mme));

}

@Test
public void testFormatter() {
    System.out.println("\njava.util.Formatter");
    double pi = Math.PI;

    StringBuilder sb = new StringBuilder(64);
    Formatter ftm = new Formatter(sb);

    // warm up
    ftm.format("%.10f", pi);
    sb.setLength(0);

    // memory measurment error
    long mme = -(getCurrentThreadAllocatedBytes() - getCurrentThreadAllocatedBytes());

    long memu0 = getCurrentThreadAllocatedBytes();

    ftm.format("%.10f", pi);

    long memu1 = getCurrentThreadAllocatedBytes();

    String val = sb.toString();

    long memu2 = getCurrentThreadAllocatedBytes();

    System.out.println(val);
    System.out.println("Format mem usage: " + (memu1 - memu0 - mme));
    System.out.println("Format + string mem usage: " + (memu2 - memu0 - 2 * mme));

}

@Test
public void testStringFormat() {
    System.out.println("\njava.lang.String.format()");

    double pi = Math.PI;

    // warmup
    String.format("%.10f", pi);

    // memory measurment error
    long mme = -(getCurrentThreadAllocatedBytes() - getCurrentThreadAllocatedBytes());

    long memu1 = getCurrentThreadAllocatedBytes();

    String val = String.format("%.10f", pi);

    long memu2 = getCurrentThreadAllocatedBytes();

    System.out.println(val);
    System.out.println("Format + string mem usage: " + (memu2 - memu1 - mme));

}

@Test
public void testDoubleFormatUtil() {
    System.out.println("\norg.apache.xmlgraphics.util.DoubleFormatUtil.formatDouble()");

    double pi = Math.PI;

    StringBuffer sb = new StringBuffer(64);

    // warmup
    DoubleFormatUtil.formatDouble(pi, 10, 10, sb);
    sb.setLength(0);

    // memory measurment error
    long mme = -(getCurrentThreadAllocatedBytes() - getCurrentThreadAllocatedBytes());

    long memu0 = getCurrentThreadAllocatedBytes();

    DoubleFormatUtil.formatDouble(pi, 10, 10, sb); 

    long memu1 = getCurrentThreadAllocatedBytes();

    String val = sb.toString();

    long memu2 = getCurrentThreadAllocatedBytes();

    System.out.println(val);
    System.out.println("Format mem usage: " + (memu1 - memu0 - mme));
    System.out.println("Format + string mem usage: " + (memu2 - memu0 - 2 * mme));

}


private long getCurrentThreadAllocatedBytes() {
    return ((com.sun.management.ThreadMXBean)ManagementFactory.getThreadMXBean()).getThreadAllocatedBytes(Thread.currentThread().getId());
}