Section 16.4. IMPLEMENTATION AND PERFORMANCE ISSUES FOR JAC


16.4. IMPLEMENTATION AND PERFORMANCE ISSUES FOR JAC

16.4.1. Implementation of JAC

JAC is entirely written in Java. The aspect weaving is performed at class load time using the bytecode engineering library BCEL [8]. This gives us the ability to weave applications whose source code is not available. In such a case, software integrators need only know the methods to relate to the pointcut definitions. The current distribution of JAC provides a set of predefined aspects for distribution (either with RMI or CORBA), persistence (JDBC or file system), GUI (Swing), authentication, transaction, consistency, load balancing, and broadcasting. A GUI console is provided for on-the-fly weaving or unweaving aspects in running applications. A CASE tool implementing the UML design notation defined in Section 16.1 is also available (see Figure 16-11).

Figure 16-11. JAC CASE tool screenshot.


The join points considered in JAC are method invocations and executions. Hooks are introduced into woven aspects whenever these events are generated. This idea is not new and has been proposed by many authors (e.g., [2]) to implement MOPs. The approach introduces a stub method for each method of a base class. These stub method introductions are done by translating the original classes so that their instances can support aspect weaving.

We investigated several techniques to perform this translation. One of these is to use compile-time reflection by using an open compiler such as OpenJava [32]. OpenJava is very powerful, allowing all kinds of code manipulation at compile time (it takes Java code and produces a translated Java code). However, since it reifies the whole syntax tree of the program, it is slow and not well suited to our simple problem. Another solution is to perform the translation at the bytecode level. Several bytecode translators are available, and most of them can work at class load time. The idea is to use a customized classloader that reads the class file and modifies the stream contents before actually defining and registering the new class within the JVM. This solution is well suited for us. First, our translation is simple and can be performed with little overhead. Second, we can translate the classes coming from a third-party program or from external libraries with no need for the Java source code.

We implemented the translation with two different bytecode translators. Javassist 1.0 [3, 4] is a reflective high-level translator that hides the complexity of the bytecode format by instantiating a load-time meta model. It is fast and easy to use. However, because of its high-level API, Javassist introduces some restrictions on the bytecode manipulations that can be done (for instance, constructors, statics, and method invocations cannot be correctly translated). As a work-around, Javassist 2.0 proposes a low-level API in addition to the high-level one.

BCEL [8] is the most popular bytecode translator. It proposes a very low-level bytecode manipulation interface that makes it very powerful (all kinds of translations can be performed). The translation we implemented with BCEL is more complex and slower than with Javassist, but the bytecode produced is of better quality.

16.4.2. Performance Measurements

The critical performance point of the JAC framework is the dynamic wrappers invocation mechanism. Since this invocation relies on reflection to achieve dynamic adding or removing of aspects, the performance overhead of JAC mainly comes from the overhead of the reflective calls. Table 16-2 shows the performance of empty method calls on regular objects and on JAC wrappable objects. These tests are performed with a benchmark program that calls several methods with different prototypes and that is available in the JAC distribution [10]. The benchmark program was run under Linux with a Pentium III 600 MHz with 256KB of cache and with the SUN Java HotSpot Client VM version 1.4.

Table 16-2. Comparative Performance Measurements for Java and JAC

Types of Calls

Number of Calls

Total Time (ms)

Time per Call

Overhead

(A) regular object calls

6,000,000

55

~9.16 ns

(B) reflective calls

60,000

47

~0.78 ms

(A) x 85

(C) JAC objects calls

60,000

61

~1.00 ms

(A) x 111

(0 wrapper)

   

(B) x 1.29

JAC (1 wrapper)

60,000

85

~1.41 ms

(C) + 41%

JAC (2 wrappers)

60,000

110

~1.83 ms

(C) + 83%

JAC (3 wrappers)

60,000

130

~2.16 ms

(C) + 116%


One can see that a call on a JAC wrappable object is comparable to a reflective call on a regular Java object (with an overhead of 29%). Each time a wrapper is added, an overhead of about 40% of the initial time is added.

Finally, the price of adaptability is high (as for reflection) compared to compiled approaches such as AspectJ. However, with real-world aspects and especially when the application is distributed, this cost is negligible compared to the added cost of remote calls. For the moment, the JAC approach is thus more suited for middle-grained wrappable objects (only business objects are made wrappable in real-word applications; technical components that need performances are not aspectized) and for distributed and adaptable programming.



Aspect-Oriented Software Development
Aspect-Oriented Software Development with Use Cases
ISBN: 0321268881
EAN: 2147483647
Year: 2003
Pages: 307

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net