创建多个“一次性”对象是否会影响性能

时间:2016-09-12 19:14:28

标签: java optimization

示例:

是这样的

public double roundDecimal(double val) {
    return Double.parseDouble(new DecimalFormat("#,##0.0").format(val));
}

double d1 = roundDecimal(val);
double d1 = roundDecimal(val);
double d1 = roundDecimal(val);
// A lot more of these...

认为是不好的做法,而不是像这样的事情?

public double roundDecimal(double val, DecimalFormat dFormat) {
    return Double.parseDouble(dFormat.format(val));
}

DecimalFormat dFormat = new DecimalFormat("#,##0.0");

double d1 = roundDecimal(val, dFormat);
double d1 = roundDecimal(val, dFormat);
double d1 = roundDecimal(val, dFormat);
// A lot more of these...

当然不同的是,我不是一遍又一遍地创建DecimalFormat对象,而是创建它一次并重新使用它。我的想法是,垃圾收集器会确保这样的事情无关紧要,但我不明白它是否足以确定。

5 个答案:

答案 0 :(得分:2)

如果你创建并丢弃了很多对象,它将使垃圾收集器更加努力。您创建的垃圾越多,GC需要停顿的次数就越多。如第二个例子中那样重用一个对象要好得多。

如果您开始重新编写代码以最小化所需的格式化程序数,请注意不要跨线程引用相同的DecimalFormat对象,请参阅the API documentation

  

十进制格式通常不同步。建议为每个线程创建单独的格式实例。如果多个线程同时访问格式,则必须在外部进行同步。

查看您正在进行此格式化的上下文,使用格式化程序进行舍入似乎非常值得怀疑,Holger's answer包含了很好的建议。

答案 1 :(得分:2)

你显然看错了。甚至是“优化的”变体

public double roundDecimal(double val, DecimalFormat dFormat) {
    return Double.parseDouble(dFormat.format(val));
}

在每次调用时创建多个对象。在API方面,format会返回您传递给String的新parseDouble实例。在表面两个操作下方,formatparseDouble创建用于完成工作的临时对象。他们的实现者没有理由担心它们,因为格式化double到十进制表示并将十进制表示解析为double的任务是如此昂贵,以至于它们超过任何东西。

很容易忽视,对于我们人类来说,十进制表示似乎是最自然的事情,但对于计算机来说,转换为十进制表示并返回非常昂贵。

但在您继续担心性能之前,您应该开始担心正确性。通常,将概念格式应用于double值是个坏主意。由于它们的内部表示与十进制数字根本不同,因此它们不能完全代表十分之一。如果你想要这种可控制的精度,BigDecimal是工作的正确工具(是的,它们是对象......)。或者您使用Formatter的结果字符串进行打印或任何其他UI演示。

除此之外,通过使用格式字符串"#,##0.0",您正在请求具有分组分隔符的字符串Double.parseDouble不期望。此外,您正在应用当前用户的区域设置的十进制格式,因此如果它不使用.作为小数分隔符,则操作将在值太小而无法进行分组时中断。因此,对于英语区域设置,传递值1234足以打破此方法,例如,德语区域设置,它将与每个值打破。

解决方法是使用相同的格式进行格式化解析:

public double roundDecimal(double val, DecimalFormat dFormat) {
    try {
        return dFormat.parse(dFormat.format(val)).doubleValue();
    } catch (ParseException ex) {
        throw new AssertionError(ex);
    }
}

但是由于创建了临时对象,这仍然会有一个灾难性的表现,不是

毕竟,如果您仍想使用double值而不是BigDecimal,则解决方案很简单:

public double roundDecimal(double val) {
    return Math.round(val*10)/10.0;
}

答案 2 :(得分:1)

一遍又一遍地实例化似乎是一种不必要的开销。如果您要处理数千个实例,这只会成为一个问题。对于这种情况,建议使用第二种方法。

答案 3 :(得分:1)

是。创建类的多个实例意味着多次执行构造函数,这肯定会花费执行时间并且可能花费内存,具体取决于构造函数的作用。你是对的,垃圾收集将恢复分配的内存,但垃圾收集也有成本。

答案 4 :(得分:1)

一般来说,是的,这种事情会影响表现。内存分配和垃圾收集通常是Java程序中非常大的时间接收。但是,在这种特殊情况下,我想编译器会看到DecimalFormat对象实际上是常量并且可以为您进行优化。