JDK-6408162 : REGRESSION: Performance Problem for Asian Fonts Larger than 19 pt in Java 1.5
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 5.0
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2006-04-04
  • Updated: 2011-02-16
  • Resolved: 2006-05-26
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6
6 b86Fixed
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.5.0_06"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_06-b05)
Java HotSpot(TM) Client VM (build 1.5.0_06-b05, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

EXTRA RELEVANT SYSTEM CONFIGURATION :
This problem manifests itself on all our computers, ranging from Pentium 4 2.8 GHz machines to Pentium 3 1.0 GHz machines.

A DESCRIPTION OF THE PROBLEM :
Java 1.5 suffers from a severe performance problem when displaying text from Asian languages at font sizes of 20 pt or larger.  To display about 50 lines of Korean text in a JTextArea on my computer takes over 12 seconds.  The problem is limited to Java 1.5 and these font sizes.  The exact same code with a Java 1.4.2 JVM will display the text almost instantly.  Changing the font size from 20 pt to 19 pt will causes Java 1.5 to display the text instantly as well.  Anything larger than 20 pt on Java 1.5 is slow.

I have been able to reproduce this problem with many different text components, including JTextArea, JTextPane, and JEditorPane.  The problem appears regardless of whether HTML or plain text is used.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and run the example program I have included, which opens a JFrame with a JTextArea inside.  When you press a button, the JTextArea is set to a sample of Korean text.

When you click the "Go" button, time how long it takes for text to appear on the screen.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The text should appear in a reasonable amount of time, probably less than 1 second.
ACTUAL -
In Java 1.5 with a font size of 20 pt, the text took 12.15 seconds on my P4 2.8 GHz computer.
In Java 1.4.2 with a font size of 20 pt, the text took less than one second to appear.
In Java 1.5 with a font size of 19 pt, the text took less than one second to appear.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;


/**
 * A simple test to demonstrate the difference in font speed for Asian fonts
 * between Java 1.4.2 and Java 1.5.  Run the program and click the "Go" button
 * to set the text in the text box to some sample text.  When run with a
 * Java 1.4.2 JVM, this happens almost instantly.  However, with Java 1.5
 * the exact same code takes almost 12 seconds to put the text on the screen.
 */
public class FontTest
{
	// A sample of Korean text.  The problem also manifests itself with other
	// Asian language fonts.
	private static String sampleText = "\uc774\ub984\uc774 " +
			"\uc0ac\uc6a9\uc790 \ubaa9\ub85d\uc5d0 \ub098\ud0c0\ub098\uc9c0 " +
			"\uc54a\ub294 \uac83\uc740 \uc5ec\ub7ec\ubd84\uc758 \uac1c\uc778 " +
			"\uc815\ubcf4\uac00 \ucef4\ud4e8\ud130\uc5d0 \uc785\ub825\ub418\uc9c0 " +
			"\uc54a\uc558\uae30 \ub54c\ubb38\uc77c \uc218 \uc788\uc2b5\ub2c8\ub2e4. " +
			"\uc774\uc804\uc5d0 \uc0ac\uc6a9\ud55c \uc801\uc774 \uc5c6\ub358 " +
			"\ucef4\ud4e8\ud130\ub85c \ud504\ub85c\uadf8\ub7a8\uc744 " +
			"\uc2e4\ud589\ud588\uc744 \ub54c \ud754\ud788 \ub098\ud0c0\ub098\ub294 " +
			"\ubb38\uc81c\uc785\ub2c8\ub2e4. \uc544\ub798 \ubc84\ud2bc\uc744 " +
			"\ud074\ub9ad\ud558\uc5ec \uc911\uc559\uc11c\ubc84\uc640 " +
			"\uc5f0\uacb0\ud55c \ud6c4 \uc0c8\ub85c\uc6b4 \uc0ac\uc6a9\uc790 " +
			"\ubaa9\ub85d\uc744 \ubc1b\uc73c\uc138\uc694. \uc774 \ub54c \ub2e4\uc2dc " +
			"\ub85c\uadf8\uc778\uc744 \ud558\uac8c \ub429\ub2c8\ub2e4.";


	// The font size to use when displaying the text.  One interesting thing about
	// this problem is that with a 19 pt font the text draws almost instantly,
	// just like it did with Java 1.4.2.  However, with a 20 pt font, it takes
	// more than 12 seconds to draw on my computer!
	public static final int FONT_SIZE = 20;


	public static void main(String[] args )
	{
		// Generate a decent amount of text to display.  The longer the string
		// the more pronounced the difference is between Java 1.4.2 and Java 1.5.
		for (int i=1; i<=5; i++)
			sampleText += sampleText;

		// Create and display a simple GUI on the event thread.
		SwingUtilities.invokeLater(new Runnable()	{
			public void run()	{
				Font font = new Font("Arial Unicode MS", Font.PLAIN, FONT_SIZE);

				final JTextArea text = new JTextArea();
				text.setLineWrap(true);
				text.setWrapStyleWord(true);
				text.setFont(font);
				text.setEditable(false);

				JButton go = new JButton("Go");
				go.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent e) {
						// Set the text in the JTextArea to the sample text.
						text.setText(sampleText);
					}
				});

				JPanel panel = new JPanel(new BorderLayout());
				panel.add(new JScrollPane(text), BorderLayout.CENTER);
				panel.add(go, BorderLayout.NORTH);

				JFrame frame = new JFrame();
				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				frame.setContentPane(panel);
				frame.setSize(800, 600);
				frame.setVisible(true);
			}
		});
	}
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
I have not found a work around for this problem.

Release Regression From : 
The above release value was the last known release where this 
bug was known to work. Since then there has been a regression.

Comments
EVALUATION Its probably not a coincidence that the getGlyphAdvance() method employs a heuristic as follows : getImageWithAdvance = (useNatives || (at.getScaleY() < 20.0 && (at.getType() & complexTX) == 0)); ie such that if a font is less than 20 pt we will always retrieve the image too. The idea was that we probably will later need an image if we need the advance now. The cut off was [arbitrarily] at 20 pt on the grounds that larger images take up more space so the storage costs need to be taken into the balance at larger sizes. But worst case this ought to double the time needed as just one more call would be needed to get the image. The real problem seems to be that this heuristic is also used to decide whether to cache the advances. That was principally intended for unusual and transient rotations but is also being applied to simple 20 pt fonts. The fix is to exclude the pt size from that decision and perhaps also to increase the size at which the image is automatically cached along with the advance. In retrospect 20 pt is too small. The comment in the code actually says its intended to be "36" rather than the 20 that is used.
12-04-2006

EVALUATION I've profiled the test using NetBeans and a recent Mustang build. The slowness comes from method sun.font.FileFontStrike.getGlyphAdvance(int). For 19 pt font it takes around 6 ms on my machine (5% of the total execution time), and for 20 pt font -- 1500 ms and 92%. This time spent in the method itself, not counting subsequent calls. Reassigning to 2D for further evaluation. I attach two NetBeans profile snapshots for 19 and 20 pt fonts. They could be loaded into NetBeans using Profile/Load Snapshot.
12-04-2006