Does Java casting introduce overhead? Why?
Asked Answered
H

5

121

Is there any overhead when we cast objects of one type to another? Or the compiler just resolves everything and there is no cost at run time?

Is this a general things, or there are different cases?

For example, suppose we have an array of Object[], where each element might have a different type. But we always know for sure that, say, element 0 is a Double, element 1 is a String. (I know this is a wrong design, but let's just assume I had to do this.)

Is Java's type information still kept around at run time? Or everything is forgotten after compilation, and if we do (Double)elements[0], we'll just follow the pointer and interpret those 8 bytes as a double, whatever that is?

I'm very unclear about how types are done in Java. If you have any reccommendation on books or article then thanks, too.

Hullo answered 31/1, 2010 at 7:10 Comment(2)
The performance of instanceof and casting is quite good. I posted some timing in Java7 around different approaches to the problem here: #16320514Thereat
This other question has very good answers #16741823Davide
P
91

There are 2 types of casting:

Implicit casting, when you cast from a type to a wider type, which is done automatically and there is no overhead:

String s = "Cast";
Object o = s; // implicit casting

Explicit casting, when you go from a wider type to a more narrow one. For this case, you must explicitly use casting like that:

Object o = someObject;
String s = (String) o; // explicit casting

In this second case, there is overhead in runtime, because the two types must be checked and in case that casting is not feasible, JVM must throw a ClassCastException.

Taken from JavaWorld: The cost of casting

Casting is used to convert between types -- between reference types in particular, for the type of casting operation in which we're interested here.

Upcast operations (also called widening conversions in the Java Language Specification) convert a subclass reference to an ancestor class reference. This casting operation is normally automatic, since it's always safe and can be implemented directly by the compiler.

Downcast operations (also called narrowing conversions in the Java Language Specification) convert an ancestor class reference to a subclass reference. This casting operation creates execution overhead, since Java requires that the cast be checked at runtime to make sure that it's valid. If the referenced object is not an instance of either the target type for the cast or a subclass of that type, the attempted cast is not permitted and must throw a java.lang.ClassCastException.

Pyromagnetic answered 31/1, 2010 at 7:14 Comment(6)
That JavaWorld article is 10+ years old, so I'd take any statements it makes about performance with a very large grain of your finest salt.Brezhnev
@skaffman, In fact I'd take any statement it makes (regardless of regarding performance of not) with a grain of salt.Feat
will it be the same case, if i don't assign the casted object to the reference and just call method on it? like ((String)o).someMethodOfCastedClass()Albur
Now the article is almost 20 years old. And the answers are also many years old. This question needs a modern answer.Braeunig
How about primitive types? I mean does, for example - casting from int to short cause similar overhead?Afebrile
@Afebrile That could be a separate question but briefly: there is no check but the data is narrowed so there is still overhead (of a different type).Kultur
P
43

For a reasonable implementation of Java:

Each object has a header containing, amongst other things, a pointer to the runtime type (for instance Double or String, but it could never be CharSequence or AbstractList). Assuming the runtime compiler (generally HotSpot in Sun's case) cannot determine the type statically a some checking needs to be performed by the generated machine code.

First that pointer to the runtime type needs to be read. This is necessary for calling a virtual method in a similar situation anyway.

For casting to a class type, it is known exactly how many superclasses there are until you hit java.lang.Object, so the type can be read at a constant offset from the type pointer (actually the first eight in HotSpot). Again this is analogous to reading a method pointer for a virtual method.

Then the read value just needs a comparison to the expected static type of the cast. Depending upon instruction set architecture, another instruction will need to branch (or fault) on an incorrect branch. ISAs such as 32-bit ARM have conditional instruction and may be able to have the sad path pass through the happy path.

Interfaces are more difficult due to multiple inheritance of interface. Generally the last two casts to interfaces are cached in the runtime type. IN the very early days (over a decade ago), interfaces were a bit slow, but that is no longer relevant.

Hopefully you can see that this sort of thing is largely irrelevant to performance. Your source code is more important. In terms of performance, the biggest hit in your scenario is liable to be cache misses from chasing object pointers all over the place (the type information will of course be common).

Potomac answered 31/1, 2010 at 11:29 Comment(2)
interesting - so does this mean that for non-interface classes if I write Superclass sc = (Superclass)subclass; that the (jit ie: load time) compiler will "staticly" put in the offset from Object in each of Superclass and Subclass in their "Class" headers and then via a simple add + compare be able to resolve things? - that is nice and fast :) For interfaces I would assume no worse than a small hashtable or btree ?Nephridium
@Nephridium For casting between classes, both the object address and the "vtbl" (table of method pointers, plus table of class hierarchy, interface cache, etc) don't change. So the [class] cast checks the type, and if it fits nothing else has to happen.Potomac
S
8

For example, suppose we have an array of Object[], where each element might have a different type. But we always know for sure that, say, element 0 is a Double, element 1 is a String. (I know this is a wrong design, but let's just assume I had to do this.)

The compiler does not note the types of the individual elements of an array. It simply checks that the type of each element expression is assignable to the array element type.

Is Java's type information still kept around at run time? Or everything is forgotten after compilation, and if we do (Double)elements[0], we'll just follow the pointer and interpret those 8 bytes as a double, whatever that is?

Some information is kept around at run time, but not the static types of the individual elements. You can tell this from looking at the class file format.

It is theoretically possible that the JIT compiler could use "escape analysis" to eliminate unnecessary type checks in some assignments. However, doing this to the degree you are suggesting would be beyond the bounds of realistic optimization. The payoff of analysing the types of individual elements would be too small.

Besides, people should not write application code like that anyway.

Sawhorse answered 31/1, 2010 at 7:37 Comment(2)
What about primitives ? (float) Math.toDegrees(theta) Will there be a significant overhead here also ?Queer
There is an overhead for some primitive casts. Whether it is significant depends on the context.Sawhorse
U
7

The byte code instruction for performing casting at runtime is called checkcast. You can disassemble Java code using javap to see what instructions are generated.

For arrays, Java keeps type information at runtime. Most of the time, the compiler will catch type errors for you, but there are cases where you will run into an ArrayStoreException when trying to store an object in an array, but the type does not match (and the compiler didn't catch it). The Java language spec gives the following example:

class Point { int x, y; }
class ColoredPoint extends Point { int color; }
class Test {
    public static void main(String[] args) {
        ColoredPoint[] cpa = new ColoredPoint[10];
        Point[] pa = cpa;
        System.out.println(pa[1] == null);
        try {
            pa[0] = new Point();
        } catch (ArrayStoreException e) {
            System.out.println(e);
        }
    }
}

Point[] pa = cpa is valid since ColoredPoint is a subclass of Point, but pa[0] = new Point() is not valid.

This is opposed to generic types, where there is no type information kept at runtime. The compiler inserts checkcast instructions where necessary.

This difference in typing for generic types and arrays makes it often unsuitable to mix arrays and generic types.

Ultrasonics answered 31/1, 2010 at 7:55 Comment(0)
C
1

In theory, there is overhead introduced. However, modern JVMs are smart. Each implementation is different, but it is not unreasonable to assume that there could exist an implementation that JIT optimized away casting checks when it could guarantee that there would never be a conflict. As for which specific JVMs offer this, I couldn't tell you. I must admit I'd like to know the specifics of JIT optimization myself, but these are for JVM engineers to worry about.

The moral of the story is to write understandable code first. If you're experiencing slowdowns, profile and identify your problem. Odds are good that it won't be due to casting. Never sacrifice clean, safe code in an attempt to optimize it UNTIL YOU KNOW YOU NEED TO.

Caryl answered 10/11, 2016 at 18:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.