Performance Checklist Object Creation

- 96 - Lazy initialization can be a useful performance-tuning technique. As usual, you should be tuning after functionality is present in your application, so I am not recommending using lazy initialization before the tuning stage. But there are places where you can change objects to be lazily initialized and make a large gain. Specifically, these are objects or variables of objects that may never be used. For example, if you need to make available a large choice of objects, of which only a few will actually be used in the application e.g., based on a users choice, then you are better off not instantiating or initializing these objects until they are actually used. An example is the char -to- byte encoding provided by the JDK. Only a few usually one of these are used, so you do not need to provide every type of encoding, fully initialized, to the application. Only the required encoding needs to be used. When you have thousands of objects that need complex initializations but only a few will actually be used, lazy initialization provides a significant speedup to an application by avoiding exercising code that may never be run. A related situation in which lazy initialization can be used for performance tuning is when there are many objects that need to be created and initialized, and most of these objects will be used, but not immediately. In this case, it can be useful to spread out the load of object initialization so you dont get one large hit on the application. It may be better to let a background thread initialize all the objects slowly or to use lazy initialization to take many small or negligible hits, thus spreading the load over time. This is essentially the same technique as for preallocation of objects see the previous section. It is true that many of these kinds of situations should be anticipated at the design stage, in which case you could build lazy initialization into the application from the beginning. But this is quite an easy change to make usually affecting just the accessors of a few classes, and so there is usually little reason to over-engineer the application prior to tuning.

4.6 Performance Checklist

Most of these suggestions apply only after a bottleneck has been identified: • Establish whether you have a memory problem. • Reduce the number of temporary objects being used, especially in loops. o Avoid creating temporary objects within frequently called methods. o Presize collection objects. o Reuse objects where possible. o Empty collection objects before reusing them. Do not shrink them unless they are very large. o Use custom conversion methods for converting between data types especially strings and streams to reduce the number of temporary objects. o Define methods that accept reusable objects to be filled in with data, rather than methods that return objects holding that data. Or you can return immutable objects. o Canonicalize objects wherever possible. Compare canonicalized objects by identity. o Create only the number of objects a class logically needs if that is a small number of objects. o Replace strings and other objects with integer constants. Compare these integers by identity. o Use primitive data types instead of objects as instance variables. o Avoid creating an object that is only for accessing a method. o Flatten objects to reduce the number of nested objects. o Preallocate storage for large collections of objects by mapping the instance variables into multiple arrays. o Use StringBuffer rather than the string concatenation operator +. - 97 - o Use methods that alter objects directly without making copies. o Create or use specific classes that handle primitive data types rather than wrapping the primitive data types. • Consider using a ThreadLocal to provide threaded access to singletons with state. • Use the final modifier on instance-variable definitions to create immutable internally accessible objects. • Use WeakReference s to hold elements in large canonical lookup tables. Use SoftReference s for cache elements. • Reduce object-creation bottlenecks by targeting the object-creation process. o Keep constructors simple and inheritance hierarchies shallow. o Avoid initializing instance variables more than once. o Use the clone method to avoid calling any constructors. o Clone arrays if that makes their creation faster. o Create copies of simple arrays faster by initializing them; create copies of complex arrays faster by cloning them. • Eliminate object-creation bottlenecks by moving object creation to an alternative time. o Create objects early, when there is spare time in the application, and hold those objects until required. o Use lazy initialization when there are objects or variables that may never be used, or when you need to distribute the load of creating objects. o Use lazy initialization only when there is a defined merit in the design, or when identifying a bottleneck which is alleviated using lazy initialization.

Chapter 5. Strings

Everyone has a logger and most of them are string pigs. —Kirk Pepperdine String s have a special status in Java. They are the only objects with: • Their own operators + and += • A literal form characters surrounded by double quotes, e.g., hello • Their own externally accessible collection in the VM and class files i.e., string pools, which provide uniqueness of String objects if the string sequence can be determined at compile time String s are immutable and have a special relationship with StringBuffer objects. A String cannot be altered once created. Applying a method that looks like it changes the String such as String.trim doesnt actually do so; instead, the method returns an altered copy of the String . Strings are also final , and so cannot be subclassed. These points have advantages and disadvantages so far as performance is concerned. For fast string manipulation, the inability to subclass String or access the internal char array can be a serious problem.

5.1 The Performance Effects of Strings

Lets first look at the advantages of the String implementation: • Compilation creates unique strings. At compile time, strings are resolved as far as possible. This includes applying the concatenation operator and converting other literals to strings. So hi7 and hi+7 both get resolved at compile time to the same string, and are identical