5

Lets assume we have three (or more) classes

public class A {}

public class B extends A {}

public class C extends B implements G {}

Assume each class has its own 20 (or more) methods.

Is casting to C vs casting to A has larger impact on performance? How does Java casting work under the hood?

Does it have to check for all methods and fields for presence by reflection when casting down?

Edit: Does the size of classes (number of fields and methods) impacts the performance when casting? I'm interested in both OpenJRE and Dalvik.

For the reference, I know that upcasting can be done with no problems.

2 Answers 2

4

The performance of the casting depends on the JVM implementation.

The JLS 5.5 only determines the requirements for the casting (which includes a recursive algorithm), but does not set any requirements upon the implementation. Actually the runtime cast rules in 5.5.3 are also determined the same way. All JVM implementations that produce th same result as the proposed algorithm are accepted as a proper JVM.

Generally, casting down to C takes a little bit more time since the JVM must examine the runtime type of the object. When casting up to A it has no reason to do the same check, since B extends A.

Actually, the JVM does not care about the number of methods and fields. It only compares the type hierarchy, the same you can examine with reflection (o.getClass())

I made a sample code as follows, one downcast, then an upcast:

Object o = new Integer(1); Integer i = (Integer) o; Object o2 = i; 

The compiled bytecode is the following:

 0 new java.lang.Integer [16] 3 dup 4 iconst_1 <-- 1 as a parameter to the constructor 5 invokespecial java.lang.Integer(int) [18] <-- constructor 8 astore_1 [o] <-- store in 'o' 9 aload_1 [o] 10 checkcast java.lang.Integer [16] <-- DOWNCAST CHECK, SPECIAL BYTECODE 13 astore_2 [i] 14 aload_2 [i] 15 astore_3 [o2] <-- WITH UPCAST NO CHECK 

So, there is a specific JVM instruction that checks the element on the top of the stack with a given class.

With upcast, there is no check at all.

The size of the classes (number of fields, methods, actual footprint) does not matter, because the casting examines the Class (the metadata, which is actually an object).

The number of hierarchy levels, and the number if implemented interfaces (if casting to an interface) does matter, because that is the traversable inheritance/implementation tree to check.

I would be surprised if there wouldn't be some kind of cache for this check.

Sign up to request clarification or add additional context in comments.

10 Comments

"since the JVM must examine the runtime type of the object" how does it examine?
Actually, I was wrong, the truth is even more native than isAssignableFrom()
So, if you are curious about the check-cast internals, maybe try to google it (I couldn't find any relevant result). I suppose that the search is done through the type hierarchy (extension and implementation) of the casted object runtime class upwards. The difference can be some processor cycles, but since it is native code, I don't think it would be relevant.
check-cast calls dvmInstanceOf(), which does a cache lookup. Last time I checked the hit rate was > 95% for framework code. So for the most part the effects of class depth won't be noticeable.
Just the sources. Start with dalvik/vm/mterp/c/OP_CHECK_CAST.cpp to see where it begins. The cache, checked in dvmInstanceofNonTrivial() in dalvik/vm/oo/TypeCheck.cpp, is implemented with some macro fun; see dalvik/vm/AtomicCache.h and .cpp. If it misses, you end up in isInstanceof(), which then splits out depending on whether you're checking vs. an interface, array, or class. For interfaces it does a linear walk through the "flattened" list of interfaces, for classes it does a recursive walk up the tree.
|
2

For a detailed architecture of checkcast (which was mentioned in other response as a JVM mechanism for downcasting) in HotSpot, take a look at this conference paper:

Fast subtype checking in the HotSpot JVM

A quote from the abstract:

In actual benchmark runs our technique performs complete subtype checks in 3 instructions (only 1 memory reference) essentially all the time. In rare instances it reverts to a slower array scan. Memory usage is moderate (6 words per class) and can be traded off for time.

So if you don't write some very low-level code with a lot of casting, the impact is negligible.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.