- The Binary Class Format
- Reflection
- Reflective Invocation
- Dynamic Proxies
- Reflection Performance
- Package Reflection
- Custom Metadata
- Onward
- Resources
3.5 Reflection Performance
Reflective invocation is slower than direct method invocation. Similarly, using dynamic proxies as a generic forwarding device results in slower code than manually crafting interface-specific delegation code does. When is the performance penalty acceptable? Programmers who are used to dynamic, interpreted runtime environments are already accustomed to dynamic programming styles and see uses for reflection everywhere. On the other hand, programmers who are used to the performance of a strongly typed, compiled language often laugh at the slow speed of reflection and reject it outright.
The truth lies somewhere in between. Reflection is best suited for writing "glue"code that sits at the boundaries between disparate classes, packages, and subsystems. If your task description includes words such as "adapter" or "decorator," then reflection may be a good fit. But reflection is not well suited for code on the critical path in an application. If your task vocabulary tends more toward "inner loop" or "heavy recursion," avoid reflection. This section presents some order-of-magnitude estimates of reflection performance, and then it gives some specific examples of where it should and should not be used.
Before directing your attention to some performance numbers, I must begin with some major caveats about their use. Evaluating a system's performance is a tricky task. Many factors impact the execution speed of even a simple Java application: virtual machine, processor speed, available memory, memory speed, OS, OS version, and other applications that are also running.
Because of these complexities, measurement is essential. Do not rely on intuition. Programmers' intuitions about performance are reliable only to within about six orders of magnitude. If you need better precision than that, you must measure. In Java, even measurement can be tricky. The virtual machine introduces yet another layer of indirection between you and the Platonic ones and zeroes. As of SDK version 1.3, the virtual machine (HotSpot) is adaptive over time, so short-run tests can grossly misrepresent long-run behaviors.
Fortunately, there is some value even in very rough measurements. For the purpose of deciding where reflection might fit into a program design, it is sufficient to have some order-of-magnitude, relative measurements. The test harness used to make the following measurements is the Timer class from the com.develop.benchmark package, which is included in the sample code for this book [Hal01].
Table 32 Rough Estimate of Reflection Performance
Operation Tested |
Time (Nearest Power of 10 nsec) |
Increment integer field |
100 |
Invoke a virtual method |
101 |
Invoke through a manual delegate |
101102 |
Reflective method invocation6 |
104-105 |
Invoke through a proxy delegate |
104105 |
Reflectively increment integer field |
104105 |
RMI call on a local machine |
105 |
Open, write, close a file |
106 |
Light travels 3000km |
107 |
Table 32 lists order-of-magnitude comparisons for reflective and nonreflective tasks. These tests were made on the HotSpot 1.3 client virtual machine, with JIT enabled, on a Pentium2-450 running Windows NT 4.0 server. However, the conclusions I plan to draw are very limited, and they should hold for a wide variety of virtual machines and hardware. First, notice that direct access to a field and direct invocation of a virtual method are very fast, in the ones and tens of nanoseconds respectively. This is getting reasonably close to a single operation per processor tick since the P450's clock ticks in a little over two nanoseconds. When you switch to reflective field access, reflective method invocation, or invocation through a dynamic proxy, you will notice that they all impose a stiff penalty, taking in the tens of microseconds to execute. This is the statistic that causes systems programmers to scoff and abandon reflection.
When you look a little further down the table, you will see some reasons for a more optimistic assessment. Though reflection may be slow compared to direct access to class members, it is quite fast compared to many other common programming tasks. A simple, cross-process method invocation on a local machine is an order of magnitude slower than a reflective invocation. Opening, writing, and closing a file is another order of magnitude slower than that. Hundreds of reflective operations could transpire in the time it takes light to travel 3,000km, which in turn, is likely faster than the time in which network packets can travel between your computer and some location on the Internet. A single online transaction involves several of these expensive operations: invoking methods cross-process, reading and writing files, and routing packets around the Internet. A single reflective method call would have a negligible impact on an Internet-based transaction.
The performance numbers could vary wildly on different virtual machines, and they are not intended to guide optimization decisions other than at the broadest level. What they do show, however, is that reflective access is not onerous if it is used sparingly in an application that is also doing interesting work. Moreover, reflection performance should improve substantially in the 1.4 SDK release. If you can easily make your design work without resorting to reflection, then do so. But do not be alarmed about performance if reflection appears to be the most convenient glue between subsystems in your application.