有效的Java—尽管创建了多个实例,但方法调用时间相同

有效的Java—尽管创建了多个实例,但方法调用时间相同,java,performance,object,static,Java,Performance,Object,Static,我正在学习有效的Java,在书的第5项中,Joshua Bloch谈到避免创建不必要的对象。一个示例演示了可变日期对象,这些对象的值一旦计算出来就永远不会被修改 这里是“坏习惯”: public Person(Date birthDate) { this.birthDate = new Date(birthDate.getTime()); } // DON'T DO THIS! public boolean isBabyBoomer() { // Unnecessary all

我正在学习有效的Java,在书的第5项中,Joshua Bloch谈到避免创建不必要的对象。一个示例演示了可变日期对象,这些对象的值一旦计算出来就永远不会被修改

这里是“坏习惯”:

public Person(Date birthDate) {
    this.birthDate = new Date(birthDate.getTime());
}

// DON'T DO THIS!
public boolean isBabyBoomer() {
    // Unnecessary allocation of expensive object
    Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
    gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
    Date boomStart = gmtCal.getTime();
    gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
    Date boomEnd = gmtCal.getTime();
    return birthDate.compareTo(boomStart) >= 0
            && birthDate.compareTo(boomEnd) < 0;
}

干杯,马库斯你的基准是错误的。使用最新的Java 7和适当的预热,这两种方法之间有了显著的区别:

Person::main: estimatedSeconds 1 = '8,42'
Person::main: estimatedSeconds 2 = '0,01'
以下是完整的可运行代码:

import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

public class Person {
    private Date birthDate;
    static Date BOOM_START;
    static Date BOOM_END;

    public Person(Date birthDate) {
        this.birthDate = new Date(birthDate.getTime());
    }

    static {
        Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
        BOOM_START = gmtCal.getTime();
        gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
        BOOM_END = gmtCal.getTime();
    }

    public boolean isBabyBoomerWrong() {
        // Unnecessary allocation of expensive object
        Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
        Date boomStart = gmtCal.getTime();
        gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
        Date boomEnd = gmtCal.getTime();
        return birthDate.compareTo(boomStart) >= 0
                && birthDate.compareTo(boomEnd) < 0;
    }

    public boolean isBabyBoomer() {
        return birthDate.compareTo(BOOM_START) >= 0
                && birthDate.compareTo(BOOM_END) < 0;
    }

    public static void main(String[] args) {
        Person p = new Person(new Date());

        for (int i = 0; i < 10_000_000; i++) {
            p.isBabyBoomerWrong();
            p.isBabyBoomer();
        }

        long startTime = System.nanoTime();

        for (int i = 0; i < 10_000_000; i++) {
            p.isBabyBoomerWrong();
        }

        double estimatedSeconds = (System.nanoTime() - startTime) / 1000000000.0;
        System.out.println(String.format("Person::main: estimatedSeconds 1 = '%.2f'", estimatedSeconds));

        startTime = System.nanoTime();

        for (int i = 0; i < 10_000_000; i++) {
            p.isBabyBoomer();
        }

        estimatedSeconds = (System.nanoTime() - startTime) / 1000000000.0;
        System.out.println(String.format("Person::main: estimatedSeconds 2 = '%.2f'", estimatedSeconds));

    }
}
import java.util.Calendar;
导入java.util.Date;
导入java.util.TimeZone;
公共阶层人士{
私人生日;
静态启动日期;
静态日期/结束;
公众人士(出生日期){
this.birthDate=新日期(birthDate.getTime());
}
静止的{
Calendar gmtCal=Calendar.getInstance(TimeZone.getTimeZone(“GMT”));
gmtCal.set(1946年,日历1月1日,0日,0日,0日);
BOOM_START=gmtCal.getTime();
gmtCal.set(1965年,日历1月1日,0日,0日,0日);
BOOM_END=gmtCal.getTime();
}
公共布尔值IsBabyBoomerError(){
//昂贵物品的不必要分配
Calendar gmtCal=Calendar.getInstance(TimeZone.getTimeZone(“GMT”));
gmtCal.set(1946年,日历1月1日,0日,0日,0日);
Date boomStart=gmtCal.getTime();
gmtCal.set(1965年,日历1月1日,0日,0日,0日);
Date boomEnd=gmtCal.getTime();
返回生日。与(boomStart)相比>=0
&&出生日期。与(婴儿期)相比<0;
}
公共布尔值isBabyBoomer(){
返回出生日期。与(BOOM_START)相比>=0
&&出生日期。与(BOOM_END)相比<0;
}
公共静态void main(字符串[]args){
人员p=新人员(新日期());
对于(int i=0;i<10_000;i++){
p、 Isbabyboomerhorrow();
p、 isBabyBoomer();
}
long startTime=System.nanoTime();
对于(int i=0;i<10_000;i++){
p、 Isbabyboomerhorrow();
}
双估计秒=(System.nanoTime()-startTime)/100000000.0;
System.out.println(String.format(“Person::main:estimatedSeconds 1='%.2f',estimatedSeconds));
startTime=System.nanoTime();
对于(int i=0;i<10_000;i++){
p、 isBabyBoomer();
}
estimatedSeconds=(System.nanoTime()-startTime)/100000000.0;
System.out.println(String.format(“Person::main:estimatedSeconds 2='%.2f',estimatedSeconds));
}
}

你的问题原来只是另一个错误的微基准

然而,在某些特殊情况下(主要是使用简单的数据保存类),确实存在一个JVM优化,它丢弃了大多数对象实例化。你可能想看看下面的链接

这里描述的方法显然不适用于您的情况,但在其他一些奇怪的情况下可能会有所不同,在这些情况下,对象实例化似乎没有占用任何时间。因此,当你实际遇到问题的工作示例时,请记住这一点:

最相关的部分:

返回复合值的典型防御性复制方法 (不要真的担心代码,只需要一个
getDistanceFrom()
方法被调用):

getLocation()
方法不知道它的调用者要做什么 对它返回的
点执行操作;它可能会保留对它的引用,
例如将其放入集合中,以便对
getLocation()
进行编码 防守。但是,在本例中,
getDistanceFrom()
不起作用 这样做;它只是将使用
一小段时间 然后扔掉它,这看起来像是对一件完美物品的浪费

智能JVM可以看到正在发生的事情并优化分配 防御性副本的副本。首先,将调用
getLocation()
内联的,以及对
getX()
getY()
的调用,导致
getDistanceFrom()
的有效行为如下:

(描述应用内联优化结果的伪代码) 到
getDistanceFrom()

此时,转义分析可以显示在 第一行永远不会从它的基本块中逃脱,而且
getDistanceFrom()
从不修改其他组件的状态。 (通过escape,我们的意思是对它的引用不会存储到堆中 或者传递给可能保留副本的未知代码。),如果
是真正的线程本地点,已知其生存期是有界的 根据分配它的基本块,它可以是 堆栈被完全分配或优化,如下所示:

用于描述优化中的外分配结果的伪代码
getDistanceFrom()

结果是,我们得到了完全相同的性能,如果 所有的场地都是公共的,同时保持着 封装和防御性复制(以及其他安全编码 技术)给我们


对于现代JVM,我希望它与新的multi-genreation GC进步没有什么不同。不要在不需要的时候创建对象;尽可能重复使用。向我们展示您的基准测试。您尝试调用该方法多少次了?关于您的基准测试,最明显的问题是您没有使用
isBabyBoomer()
方法的输出。因此,JVM意识到了这一点,并简单地丢弃了所有这些调用。另外,您至少应该加热JVM。使用caliper实现您的微基准测试。感谢您的精彩解释!
Person::main: estimatedSeconds 1 = '8,42'
Person::main: estimatedSeconds 2 = '0,01'
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

public class Person {
    private Date birthDate;
    static Date BOOM_START;
    static Date BOOM_END;

    public Person(Date birthDate) {
        this.birthDate = new Date(birthDate.getTime());
    }

    static {
        Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
        BOOM_START = gmtCal.getTime();
        gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
        BOOM_END = gmtCal.getTime();
    }

    public boolean isBabyBoomerWrong() {
        // Unnecessary allocation of expensive object
        Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
        Date boomStart = gmtCal.getTime();
        gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
        Date boomEnd = gmtCal.getTime();
        return birthDate.compareTo(boomStart) >= 0
                && birthDate.compareTo(boomEnd) < 0;
    }

    public boolean isBabyBoomer() {
        return birthDate.compareTo(BOOM_START) >= 0
                && birthDate.compareTo(BOOM_END) < 0;
    }

    public static void main(String[] args) {
        Person p = new Person(new Date());

        for (int i = 0; i < 10_000_000; i++) {
            p.isBabyBoomerWrong();
            p.isBabyBoomer();
        }

        long startTime = System.nanoTime();

        for (int i = 0; i < 10_000_000; i++) {
            p.isBabyBoomerWrong();
        }

        double estimatedSeconds = (System.nanoTime() - startTime) / 1000000000.0;
        System.out.println(String.format("Person::main: estimatedSeconds 1 = '%.2f'", estimatedSeconds));

        startTime = System.nanoTime();

        for (int i = 0; i < 10_000_000; i++) {
            p.isBabyBoomer();
        }

        estimatedSeconds = (System.nanoTime() - startTime) / 1000000000.0;
        System.out.println(String.format("Person::main: estimatedSeconds 2 = '%.2f'", estimatedSeconds));

    }
}
public class Point {
    private int x, y;
    public Point(int x, int y) {
        this.x = x; this.y = y;
    }
    public Point(Point p) { this(p.x, p.y); }
    public int getX() { return x; }
    public int getY() { return y; }
}

public class Component {
    private Point location;
    public Point getLocation() { return new Point(location); }
    public double getDistanceFrom(Component other) {
        Point otherLocation = other.getLocation();
        int deltaX = otherLocation.getX() - location.getX();
        int deltaY = otherLocation.getY() - location.getY();
        return Math.sqrt(deltaX*deltaX + deltaY*deltaY);
    }
}
public double getDistanceFrom(Component other) {
    Point otherLocation = new Point(other.x, other.y);
    int deltaX = otherLocation.x - location.x;
    int deltaY = otherLocation.y - location.y;
    return Math.sqrt(deltaX*deltaX + deltaY*deltaY);
}
public double getDistanceFrom(Component other) {
    int tempX = other.x, tempY = other.y;
    int deltaX = tempX - location.x;
    int deltaY = tempY - location.y;
    return Math.sqrt(deltaX*deltaX + deltaY*deltaY);
}