|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 4 of 6
By using some one-time allocations, we can create a set of objects required for formatting, then reuse these dedicated objects as needed. This set of objects is then owned by the code which uses them. For example, if we apply this approach using instance variables, so that each instance of the containing class owns a unique copy of the objects, we'd have something like this:
...
// Allocate dedicated time formatting objects as member variables.
private final Date convertDate = new Date();
private final DateFormat convertFormat = DateFormat.getDateInstance();
private final StringBuffer convertBuffer = new StringBuffer();
private final FieldPosition convertField = new FieldPosition(0);
...
// Get the default string for time.
long time = ...;
convertDate.setTime(time);
convertBuffer.setLength(0);
StringBuffer output =
dateFormatter.format(convertDate, convertBuffer, convertField);
String display = output.toString();
...
This code is considerably longer than the original statement, but executes much more quickly because the only object constructed each time through is the output string. In a test run with 100,000 iterations, this code took only 8 seconds to execute, as opposed to 50 seconds for the original technique. This approach does incur some additional memory usage as the price of the speed advantage, since the objects used for the formatting are kept permanently allocated instead of being freed when not in use. But if the code is executed frequently, it's a very good trade-off.
It's worth pointing out that the same technique applies if you have an inner loop within a method doing a large number of iterations. You may not need to have a set of the objects used within the loop permanently owned by the object containing the method, but you can still move the object allocations outside the loop so that they're only done once. In this case, our example code might look like this:
// Allocate objects to be used inside loop.
Date date = new Date();
DateFormat formatter = DateFormat.getDateInstance();
StringBuffer buffer= new StringBuffer();
FieldPosition field = new FieldPosition(0);
// Execute the loop.
for (...) {
// Get the default string for time.
long time = ...;
date.setTime(time);
buffer.setLength(0);
StringBuffer output = formatter.format(date, buffer, field);
String display = output.toString();
}
This dedicated object reuse technique often works especially well in combination with the approach of using primitive values
in place of objects, as described above. A dedicated object can be initialized from the primitive values and passed to class
library methods that expect an object of the original type. The use of the dedicated Date object above provides an example of this.
If we have a set of owned objects, and multiple threads can execute the code that uses the objects concurrently, we need to
prevent conflicts between the different threads' usage of the objects. The easiest way to accomplish this is to designate
one of the objects as the lock for the whole set, and enclose the code using the set of objects within a synchronized block on the lock object. This adds the overhead of a locking operation for each use of the owned objects, but the locking
overhead is low in comparison to the object creation time.