Avoiding Garbage Collection Object Creation

- 90 - else if canonical_i = Integer ref.get == null { has been set, but was garbage collected, so recreate and set it now Include a print to see that we are resetting the Integer System.out.printlnResetting integer + i; canonical_i = new Integeri; canonicalIntegers.setElementAtnew WeakReferencecanonical_i, i; } else clause not needed, since the alternative is that the weak ref was present and not garbage collected, so we now have our canonical integer return canonical_i; } }

4.2.4.4 Enumerating constants

Another canonicalization technique often used is replacing constant objects with integers. For example, rather than use the strings female and male, you should use a constant defined in an interface: public interface GENDER { public static final int FEMALE=1; public static final int MALE=2; } Used consistently, this enumeration can provide both speed and memory advantages. The enumeration requires less memory than the equivalent strings and makes network transfers faster. Comparisons are faster too, as the identity comparison can be used instead of the equality comparison. For example, you can use: this.gender == FEMALE; instead of: this.gender.equalsfemale;

4.3 Avoiding Garbage Collection

The canonicalization techniques Ive discussed are one way to avoid garbage collection: fewer objects means less to garbage-collect. Similarly, the pooling technique in that section also tends to reduce garbage-collection requirements, partly because you are creating fewer objects by reusing them, and partly because you deallocate memory less often by holding on to the objects you have allocated. Of course, this also means that your memory requirements are higher, but you cant have it both ways. Another technique for reducing garbage-collection impact is to avoid using objects where they are not needed. For example, there is no need to create an extra unnecessary Integer to parse a String containing an int value, as in: String string = 55; int theInt = new Integerstring.intValue Instead, there is a static method available for parsing: - 91 - int theInt = Integer.parseIntstring; Unfortunately, some classes do not provide static methods that avoid the spurious intermediate creation of objects. Until JDK Version 1.2, there were no static methods that allowed you to parse strings containing floating-point numbers to get double s or float s. Instead, you needed to create an intermediate Double object and extract the value. Even after JDK 1.2, an intermediate FloatingDecimal is created, but this is arguably due to good abstraction in the programming design. When a class does not provide a static method, you can sometimes use a dummy instance to repeatedly execute instance methods, thus avoiding the need to create extra objects. The primitive data types in Java use memory space that also needs reclaiming, but the overhead in reclaiming data-type storage is smaller: it is reclaimed at the same time as its holding object and so has a smaller impact. Temporary primitive data types exist only on the stack and do not need to be garbage-collected at all: see Section 6.3 for more on this. For example, an object with just one instance variable holding an int is reclaimed in one object reclaim, whereas if it holds an Integer object, the garbage collector needs to reclaim two objects. Reducing garbage collection by using primitive data types also applies when you can hold an object in a primitive data-type format rather than another format. For example, if you have a large number of objects each with a String instance variable holding a number e.g., 1492, 1997, it is better to make that instance variable an int data type and store the numbers as int s, provided that the conversion overheads do not swamp the benefits of holding the values in this alternative format. Similarly, you can use an int or long to represent a Date object, providing appropriate calculations to access and update the values, thus saving an object and the associated garbage overhead. Of course, you have a different runtime overhead instead, as those conversion calculations may take up more time. A more extreme version of this technique is to use arrays to map objects: for example, see Section 11.8 . Towards the end of that example, one version of the class gets rid of node objects completely, using a large array to map and maintain all instances and instance variables. This leads to a large improvement in performance at all stages of the object life cycle. Of course, this technique is a specialized one that should not be used generically throughout your application, or you will end up with unmaintainable code. It should be used only when called for and when it can be completely encapsulated. A simple example is for the class defined as: class MyClass { int x; boolean y; } This class has an associated collection class that seems to hold an array of MyClass objects, but that actually holds arrays of instance variables of the MyClass class: class MyClassCollection { int[] xs; boolean[] ys; public int getXForElementint i {return xs[i];} public boolean getYForElementint i {return ys[i];} If possible avoid having to declare element access like the following method: public MyClass getElementint i {return new MyClassxs[i], ys[i];} } - 92 - An extension of this technique flattens objects that have a one-to-one relationship. The classic example is a Person object that holds a Name object, consisting of first name and last name and collection of middle names, and an Address object, with street, number, etc. This can be collapsed down to just the Person object, with all the fields moved up to the Person class. For example, the original definition consists of three classes: public class Person { private Name name; private Address address; } class Name { private String firstName; private String lastName; private String[] otherNames; } class Address { private int houseNumber; private String houseName; private String streetName; private String town; private String area; private String greaterArea; private String country; private String postCode; } These three classes collapse into one class: public class Person { private String firstName; private String lastName; private String[] otherNames; private int houseNumber; private String houseName; private String streetName; private String town; private String area; private String greaterArea; private String country; private String postCode; } This results in the same data and the same functionality assuming that Address es and Name s are not referenced by more than one Person . But now you have one object instead of three for each Person . Of course, this violates the good design of an application and should not be used as standard, only when absolutely necessary. Finally, here are some general recommendations that help to reduce the number of unnecessary objects being generated. These recommendations should be part of your standard coding practice, not just performance-related: • Reduce the number of temporary objects being used, especially in loops. It is easy to use a method in a loop that has side effects such as making copies, or an accessor that returns a copy of some object you only need once. • Use StringBuffer in preference to the String concatenation operator + . This is really a special case of the previous point, but needs to be emphasized. • Be aware of which methods alter objects directly without making copies and which ones return a copy of an object. For example, any String method that changes the string such as - 93 - String.trim returns a new String object, whereas a method like Vector.setSize does not return a copy. If you do not need a copy, use or create methods that do not return a copy of the object being operated on. • Avoid using generic classes that handle Object types when you are dealing with basic data types. For example, there is no need to use Vector to store int s by wrapping them in Integer s. Instead, implement an IntVector class that holds the int s directly.

4.4 Initialization