有效的Java—尽管创建了多个实例,但方法调用时间相同
我正在学习有效的Java,在书的第5项中,Joshua Bloch谈到避免创建不必要的对象。一个示例演示了可变日期对象,这些对象的值一旦计算出来就永远不会被修改 这里是“坏习惯”:有效的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
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);
}