Performance Improvement Techniques for JavaFX Applications
- Skill Level Intermediate
- Product JavaFX
- Key Features Performance
- Last Updated May 2009
This article contains a collection of recommendations and tips aimed at improving the performance of your JavaFX applications.
Turning Off Antialiasing Unless It Is Really Needed
Antialiasing is a technique used for smoothing the edges of curved lines and eliminating distortions of graphical objects to improve the visual appeal of the interface. In JavaFX Script programming language, antialiasing is applied by default to all graphical shapes, which means that the smooth instance variable is set to true.
However, the use of antialiasing has a side effect, which appears in a more complicated rendering. For this reason, you should turn off antialiasing unless it is really needed.
For example, you do not need to apply antialiasing for rectangles unless they are rotated. In particular, smoothing is absolutely not needed when a rectangle is used to limit visibility.
Circle {
clip: Rectangle {
smooth: false
}
}
Work With Images: Avoid Duplication
Look at the code for creating an Image object.
image: Image {
url: "http://my.server.com/image.png"
}
When the Image object is created, it invokes the following actions:
- Loading a file
- Allocating memory for the raster image
- Decoding the image into the raster format
Depending on the size and location of the image, creating extra copies of the image might significantly diminish your application performance. You are strongly encouraged to avoid duplicating.
If the same image is used in several places, create a single Image object and further reuse it in different ImageView instances as shown in the following code.
var img = Image {
url: "http://my.server.com/image.png"
}
for (i in [1..count]) ImageView {
image: img
}
When used improperly, data binding is another typical case that leads to unnecessary cloning of images. Look at the following code and try to think of ways to improve it.
var node: Node;
Group{
content: bind [
node
ImageView{
image: Image {
url: "http://my.server.com/image.png"
}
}
]
}
As you can see, this code loads an image on each change of the node object. To improve the code, have the image loaded only once.
var node: Node;
var img: Image {
url: "http://my.server.com/image.png"
}
Group{
content: bind [
node
ImageView{
image: img
}
]
}
Work With Images: Save on Memory
For most raster images, 4 bytes are used to set the color for each point. This means that a 2-MB file with a 6-megapixel JPEG photo, stored as a raster image, occupies about 24MB of the main memory.
If you do not need to render the image in the full resolution of 2800x2100, but in 800x600 resolution, you can specify the expected dimensions on the image loading. Thus, by adding a couple lines of code you can significantly save on memory footprint (a raster image of 800x600 occupies about 2MB).
Similarly, if you operate with a collection of images while the interface mostly uses image thumbnails, it is a good practice to create reduced raster copies to be used as thumbnails and postpone creation of full images until they are actually needed.
Image{
width: 150
height: 100
url: "http://my.server.com/image.png"
}
Animation: Avoid the Redundant Work
Most modern monitors perform repainting with a frequency of no more than 60 times per second, which is quite enough, as the human eye can recognize only 24 frames per second. For this reason, the more frequent animation is considered a waste of computing resources.
JavaFX Script enables developers to optimize repainting code and avoid redundant work. However, you cannot define programmatically which part of your JavaFX code should not be executed. For example, if your application contains the Timeline object with a frequency of 1/1000 second, which only prepares the scenes to be visualized, then only 6 percent of the time spent on its processing will be a good use of resources!
Avoid unnecessary frequency of animation. This will help to save computing resources for the more important tasks.
def clip = Timeline {
repeatCount: Timeline.INDEFINITE
keyFrames:
KeyFrame {
time: 40ms // 25 fps
action: function () : Void {...}
}
}
Cache Complex Objects
The JavaFX Script programming language enables you to effectively create scenes from simple graphic elements with applied transformations and effects. For example, the following code creates two complex objects: a circle with the lighting effect and a background element consisting of several rectangular bricks.
var ball : Circle = Circle {
centerX: 250 centerY: 250 radius:100
fill: Color.GREEN
stroke: Color.YELLOW
effect: Lighting {}
}
var brickBackground = Group {
for(i in [0..25]) {
for (j in [0..25]) {
Group {
content:
for (k in [0..7]) {
Rectangle {
x: i * 20 + k
y: j * 20 + k
width: 20 - 2 * k
height: 20 - 2 * k
fill: Color.BLUE
stroke: Color.RED
effect: Lighting {}
}
}
}
}
}
}
}
JavaFX Script enables you to easily animate the ball object against the background. (For more information about animation, see Creating Animated Objects.)
However, this code is not optimal. It is important to understand that complex objects are by default repainted over from their constituent pieces. Repainting of complex objects that consist of a large amount of elements with rich effects might be quite an expensive operation.
In this example, repainting is a waste of resources because both the ball and brickBackground objects always look the same.
The correct approach is to apply caching to the static objects, that is, to set the cache attribute to true. The JavaFX runtime caches the complex object in the interim image and uses this image for repeated repainting.
var ball : Circle = Circle {
centerX: 250 centerY: 250 radius:100
fill: Color.GREEN
stroke: Color.YELLOW
effect: Lighting {}
cache: true
}
Note that caching does not work for some transformations such as rotation. If you need to render images that are always rotated, use your favorite graphical tool to create rotated images and use them during runtime.
Define Variables With def Instead of var
Whenever you need to define a variable whose value is set once when the object is created and never changed, define the variable by using the def keyword. Make the variable script-private as much as possible to allow compiler optimizations.
Avoid Deep-Nested Structures
When you build the graphical scene of your application, remember that making too deep-nested structures might impede the overall performance. The general recommendation is to keep the scene graph as small as possible. Whenever you need to hide some elements, remove them from the scene graph instead of setting their visible variable to false.
Avoid Unreasonable Bindings
Data binding is a powerful feature of the JavaFX Script language, which enables the developer to define the relationship between the two variables. Whenever one variable changes, another variable is automatically updated. However, make sure that you use binding intentionally, as this is quite an expensive operation. Try to avoid unreasonable bindings, such as binding to variables whose values do not change.
Avoid Using Strokes
If you want to change the color of text or draw a filled rectangle with a border, the preferred way to do that is to use the fill variable rather than the stroke variable. It is faster to render two filled rectangles by placing one rectangle on top of the other.
Related Links
- Technical Tip: How to Improve JavaFX Application Startup Time
- Technical Tip: Running Time-Consuming Operations in the Background Mode
- Article: JavaFX Mobile Applications Best Practices for Improving Performance
- Article: Improving Media Performance
We welcome your participation in our community. Please keep your comments civil and on point. You may optionally provide your email address to be notified of repliesyour information is not used for any other purpose. By submitting a comment, you agree to these Terms of Use.
Irina Fedortsova
Technical Writer, Sun Microsystems