Effective Java - Same method invocation time despite creating multiple instances

前端 未结 2 1920
滥情空心
滥情空心 2021-01-15 04:55

I\'m studying Effective Java and in Item 5 of the Book, Joshua Bloch talks about the avoidance of creating unnecessary objects. An example demonstrates mutable Date objects

2条回答
  •  野趣味
    野趣味 (楼主)
    2021-01-15 05:46

    Your question turned out to be just another case of a wrong microbenchmark.

    However, in some special cases (mostly with simple data-holding classes), there really is a JVM optimization that discards most of object instantiations. You might want to look at the links below.

    The methods described there are obviously not applicable in your case, but it might make the difference in some other strange cases where object instantiation just doesn't seem to taky any time. So remember this for when you actually come across the working example of your question:

    • Scalar replacement: Automatic stack allocation in the java virtual machine (stefankrause.net)
    • Allocation is faster than you think, and getting faster (ibm.com/developerworks).

    The most relevant part:

    Typical defensive copying approach to returning a compound value (don't really worry about the code, it's just that a Point will be instantiated and accessed to via getter methods when the getDistanceFrom() method is invoked):

    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);
        }
    }
    

    The getLocation() method does not know what its caller is going to do with the Point it returns; it might retain a reference to it, such as putting it in a collection, so getLocation() is coded defensively. However in this example, getDistanceFrom() is not going to do this; it is just going to use the Point for a short time and then discard it, which seems like a waste of a perfectly good object.

    A smart JVM can see what is going on and optimize away the allocation of the defensive copy. First, the call to getLocation() will be inlined, as will the calls to getX() and getY(), resulting in getDistanceFrom() effectively behaving like this:

    (Pseudocode describing the result of applying inlining optimizations to getDistanceFrom())

    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);
    }
    

    At this point, escape analysis can show that the object allocated in the first line never escapes from its basic block and that getDistanceFrom() never modifies the state of the other component. (By escape, we mean that a reference to it is not stored into the heap or passed to unknown code that might retain a copy.) Given that the Point is truly thread-local and its lifetime is known to be bounded by the basic block in which it is allocated, it can be either stack-allocated or optimized away entirely, as shown in here:

    Pseudocode describing the result of optimizing away allocation in getDistanceFrom():

    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);
    }
    

    The result is that we get exactly the same performance as we would if all the fields were public while retaining the safety that encapsulation and defensive copying (among other safe coding techniques) give us.

提交回复
热议问题