/* @(#)TileCache.txt 1.10 00/10/04 16:57:25 */ JAI TileCache Proposal ~~~~~~~~~~~~~~~~~~~~~~ 1. Motivation -------------- The purpose of this proposal is to address some of the problems associated with the Tile Cache implementation in JAI 1.0.2 along with a presentation of proposed new features. Also, a new set of diagnostics capabilities has been added which can provide information to application developers who need an in-depth view of the operation of the tile cache. 2. Objective ------------- The new design changes are intended to improve memory usage, performance, flexibility and efficiency, reduce problems associated with "OutOfMemoryError" conditions, properly handle operator chains and single repetative operations with regard to garbage collection. 3. Proposed Changes -------------------- 3.1 Classes subject to Modification ------------------------------------------- core: JAI.java TileCache.java OpImage.java CachedTile.java (new public interface) com.sun: SunCachedTile.java (name changed from CachedTile) SunTileCache.java CacheDiagnostics.java (new) NOTE: classes under com.sun are declared final and therefore are not extensible. The SunTileCache class is public, but the SunCachedTile class is package private. 3.2 Initial Design Changes ------------------------------------------- Code in SunCachedTile.java and SunTileCache.java has been consolodated and private methods have been manually inlined to improve performance. The default memory capacity for the cache is set to 16meg. This value can be changed programmatically. If the cache becomes full, 1/4 of the allocated memory capacity will be released, by default. A new parameter and methods to change this value have been added. In JAI 1.0.2, the cached tile owner (RenderedImage) and (Raster) tile were hard references. This meant that the only way for tiles to be garbage collected was if the tile cache became full and the memory control routine forcibly removed references to the cached tiles. This caused an increase in "OutOfMemoryErrors" and performance problems for operations that didn't get reused or that fell "out of scope". This also could cause constant flushing (via memory control) of the tile cache. Even though the OpImage class finalize() method was being called to remove tiles, this mechanism did not work because the memory controller removed the references before the finalize method was called. The finalize method never found the tiles it was trying to remove because they were already removed. In JAI 1.1, the tile (Raster) is left as a hard reference but the owner (RenderedImage) is now a WeakReference. When the operation is no longer referenced or it goes out of scope, the operation becomes eligible for garbage collection. When the operation is garbage collected, it's finalize() method will be called, which removes the hard references to the cached tiles, at which time, they become eligible for garbage collection. If a getTile() causes an OutOfMemoryError, the tile cache will be flushed and all cached tiles will get garbage collected. This significantly reduces OutOfMemoryErrors due to the cache. They can still occur in other parts of an application and should be handled appropriately. With this design, operation chains behave correctly and the cases where repetitive operations, such as brightness adjustment (where the current RenderedImage does not depend on the previous one) occur, the previous "out of scope ops" get garbage collected. This reduces the memory footprint of a program. The finalize() method in OpImage was left essentially unchanged, but working properly. 3.0 New Features ----------------- 3.1. New Core Methods Propose adding the following two methods to the JAI class: static final void enableDefaultTileCache() static final void disableDefaultTileCache() These routines affect the KEY_TILE_CACHE hints for the default instance of the tile cache, only. The cache is enabled by default. Enabling the tile cache puts the default instance of JAI's rendering hints KEY_TILE_CACHE to the default instance of the Tile Cache. Disabling the default tile cache will first flush all tiles from the tile cache, then remove the KEY_TILE_CACHE rendering hint from JAI. It is recommended that these routines be set once at the beginning of an application to establish the base use of the tile cache. Note that these methods only apply to the JAI default tile cache. Other methods are available to control caching of individual operators or custom caches. Propose adding a new RenderingHint: KEY_TILE_CACHE_METRIC The default "Object" value for this key is null. The value can be set for a particular JAI operation via the rendering hints passed to a JAI.create() method. This parameter provides a means to add a "compute cost metric" or priority to a tile. This may be used to design custom cache management schemes. For example, a developer might want to remove tiles from a cache based on compute time/cost for a set of operations. Low cost (fast) operations could be removed before high cost (compute intensive) operations. If this parameter is interpreted as priority, then low values would indicate low priority. This allows a single rendering hint key to be used to mean either cost or priority. Propose adding new public methods to the TileCache interface: void setMemoryThreshold(float memoryThreshold) float getMemoryThreshold() void memoryControl() void add(RenderedImage owner, int tileX, int tileY, Raster data, Object tileCacheMetric) Raster[] getTiles(RenderedImage owner) void removeTiles(RenderedImage owner) void setTileComparator(Comparator comparator) Comparator getTileComparator() The memory threshold value is a floating point number which ranges from 0.0 to 1.0. It represents the fractional value of the memory capacity to retain if the cache becomes full. By default, it is set to 0.75, which means that if the cache memory capacity is reached, 25% of the tiles will be released from the cache based upon a least recently used algorithm. The memoryControl() method allows application developers to clear the cache to the level set by the memory threshold. Note that flushing the cache removes all references to the tiles at which point they become eligible for garbage collection. An additional "add" method has been added to the TileCache interface which takes an Object representing the cost of computing a tile. This value is "user" supplied. JAI makes no assumptions about the value of this parameter, and by default it is set to null. The getTiles() and removeTiles() methods retrieve/remove all of the tiles associated with the specific image that owns the tiles. The setTileComparator() method sets a Comparator which imposes an order on the CachedTiles stored in the TileCache. This ordering is used in memoryControl() to determine the sequence in which tiles will be removed from the TileCache so as to reduce the memory to the level given by the memory threshold. The Objects passed to the compare() method of the Comparator will be instances of CachedTile. CachedTiles will be removed from the TileCache in the ascending order imposed by this Comparator. If no Comparator is currently set, the TileCache should use an implementation-dependent default ordering. In the Sun Microsystems, Inc., implementation of TileCache, this ordering is the least recently used ordering, i.e., the tiles least recently used will be removed first by memoryControl(). The getTileComparator() method returns the currently set comparator, which may be null. Propose adding a new public CachedTile interface: RenderedImage getOwner() Raster getTile() Object getTileCacheMetric() long getTileTimeStamp() int getAction() The getOwner() method returns the RenderedImage to which a tile belongs. The getTile() method returns the actual tile as a Raster. The getTileCacheMetric() method returns the tile computation cost as set via the mapping of JAI.KEY_TILE_CACHE_METRIC in the RenderingHints supplied to the operation which created the image to which this tile belongs. The getTileTimeStamp() method returns the time stamp of the tile. Larger values should indicate more recent tiles. Implementations may implement this time stamp as an actual time value or as a counter. In the Sun Microsystems implementation of SunTileCache it is implemented as a counter. The getAction() method returns an integer value keyed to the method which triggered a notify event. This provides a means to identify which action for this tile occured. Examples are add tile, remove tile and update tile. 3.2. Tile Cache Diagnostics The Sun specific implementation of the TileCache class will extend the Observable class. This will allow application developers to monitor tile cache activity externally. Because of the implementation specific nature of these routines and the goals of the JAI1.1 release, they are not considered part of JAI's core functionality. This is currently an uncommited interface. A new com.sun CacheDiagnostics interface will be added that includes following new methods used for diagnostic purposes: public void enableDiagnostics(); public void disableDiagnostics(); public long getCacheTileCount(); public long getCacheMemoryUsed(); public long getCacheHitCount(); public long getCacheMissCount(); public void resetCounts(); // resets hit,miss counts The SunTileCache class will implement this interface also, along with Observable. The purpose of these classes is to facilitate the design and analysis of an application's use of the tile cache. The tile cache now notifies it's observers when a tile is added, removed or updated if diagnostics are enabled. These are referred to as actions. The cached tile that triggered the action is provided as an argument to the notifyObservers() method. Pertinent cache information is made available to the observers, so that cache usage can be monitored. Currently, five actions are defined (as integers) as follows: add - a tile was added to the cache remove - a tile was removed from the cache remove_from_flush - all tiles were removed remove_from_memcon - a percentage of the tiles were removed by the memory controller update_from_add - an attempt was made to add a tile that is already in the cache update_from_gettile - a get tile request found a tile in the cache 4.0 Deprecated Routines ------------------------ The following methods will be deprecated in the TileCache class: setTileCapacity() getTileCapacity() In JAI 1.0.2, they are non-functional.