JDK-6926209 : ClassCastException in sun.font.Font2D
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 6,6u21
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: solaris_8,windows_7
  • CPU: x86
  • Submitted: 2010-02-13
  • Updated: 2011-03-14
  • Resolved: 2011-02-16
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 7
7 b77Fixed
Related Reports
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.6.0.05"
Java(TM) SE Runtime Environment (build 1.6.0.05-jinteg_14_oct_2009_01_44-b00)
Java HotSpot(TM) 64-Bit Server VM (build 14.2-b01-jre1.6.0.05-rc5, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
HP-UX drsaca05 B.11.31 U ia64 1459067606 unlimited-user license

A DESCRIPTION OF THE PROBLEM :
This problem arises because the strikeCacheMap in Font2D is populated by calling StrikeCache.getStrikeRef(FontStrike strike, boolean weak) which can return a SoftReference, WeakReference, or a StrikeCache.DisposableStrike.  Font2D assumes everything in strikeCacheMap implements StrikeCache.DisposableStrike and will occasionally throw a ClassCastException as a result.

I do not know why I'm running into the case where a FontStrike does not have a disposer, but this issue seems resolved after specifying java.awt.headless=true.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the provided test program on HP-UX.  After about 5 minutes, the program should throw the ClassCastException.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No error.
ACTUAL -
java.lang.ClassCastException: java.lang.ref.SoftReference cannot be cast to
sun.font.StrikeCache$DisposableStrike
    at sun.font.Font2D.getStrike(Font2D.java:308)
    at sun.font.Font2D.getStrike(Font2D.java:262)
    at sun.font.CompositeStrike.getStrikeForSlot(CompositeStrike.java:59)
    at sun.font.CompositeStrike.getFontMetrics(CompositeStrike.java:75)
    at
sun.font.FontDesignMetrics.initMatrixAndMetrics(FontDesignMetrics.java:345)
    at sun.font.FontDesignMetrics.<init>(FontDesignMetrics.java:336)
    at sun.font.FontDesignMetrics.getMetrics(FontDesignMetrics.java:284)
    at sun.java2d.SunGraphics2D.getFontMetrics(SunGraphics2D.java:764)

ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.lang.ClassCastException: java.lang.ref.SoftReference cannot be cast to
sun.font.StrikeCache$DisposableStrike
    at sun.font.Font2D.getStrike(Font2D.java:308)
    at sun.font.Font2D.getStrike(Font2D.java:262)
    at sun.font.CompositeStrike.getStrikeForSlot(CompositeStrike.java:59)
    at sun.font.CompositeStrike.getFontMetrics(CompositeStrike.java:75)
    at
sun.font.FontDesignMetrics.initMatrixAndMetrics(FontDesignMetrics.java:345)
    at sun.font.FontDesignMetrics.<init>(FontDesignMetrics.java:336)
    at sun.font.FontDesignMetrics.getMetrics(FontDesignMetrics.java:284)
    at sun.java2d.SunGraphics2D.getFontMetrics(SunGraphics2D.java:764)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package fonttest;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;

public class Main {

    private static String[] fontNames;
    private static final int SIZE = 100;

    private static void _resetFont(Graphics g) {
        Font font = g.getFont();
        int numfonts = fontNames.length;
        for (int j = 0; j < numfonts; ++j) {
            String name = fontNames[j];
            for (int i = 1; i < 100; ++i) {
                Font f = new Font(name, font.getStyle(), i);
                g.setFont(f);
                FontMetrics fm = g.getFontMetrics();
            }
        }
        System.gc();
        Thread.yield();

        g.setFont(font);
    }

    public static void paint(Graphics g) {
        Font font = new Font("Arial", Font.PLAIN, 9);
        Rectangle r = new Rectangle(0, 0, SIZE, SIZE);
        g.drawLine(0, 0, r.width - 1, r.height - 1);
        g.setColor(new Color(255, 127, 0));
        g.drawLine(0, r.height - 1, r.width - 1, 0);
        g.setFont(font);
        _resetFont(g);
        g.drawString("Test", 32, 8);
    }

    public static void main(String[] args) {
        System.setProperty("java.awt.headless", "false");

       GraphicsEnvironment ge =
        GraphicsEnvironment.getLocalGraphicsEnvironment();
        System.out.println("Headless mode: " + ge.isHeadless());

        fontNames = ge.getAvailableFontFamilyNames();

        BufferedImage image = new BufferedImage(SIZE, SIZE, BufferedImage.TYPE_4BYTE_ABGR);
        Graphics2D gr = image.createGraphics();
        while (true) {
            try {
                paint(gr);
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(0);
            }
        }
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
set java.awt.headless=true

Comments
EVALUATION The code should not have made that assumption. But, the referent would always be a DisposableStrike except when there is no native object to dispose. This is extremely rare as it likely implies a problem in creating the strikes. See 4882741. I think fixing this bug was the code that introduced the possibility this would not be a DisposableStrike. So its been like this pretty much since early 1.5 pre-beta days. However this will no longer be an issue as in the fix for 6899078, the casting code is deleted. After some consideration there as to why that code was needed at all it was concluded it wasn't necessary. The reason for the code was concern that removing it from the map would make it unreachable and would not be enqueued for disposal, so we explicitly dispose it. However since if we find the ref is cleared then we know its already enqueued so this doesn't matter. I will mark this as fixed in the same build as we fixed 6899078.
16-02-2010