changeset 57342:269d810313dc

8209113: Use WeakReference for lastFontStrike for created Fonts Reviewed-by: serb, jdv
author prr
date Thu, 05 Dec 2019 13:24:52 -0800
parents 002b849de829
children aff43b3630be
files src/java.desktop/share/classes/sun/font/Font2D.java src/java.desktop/share/classes/sun/font/FontStrikeDisposer.java src/java.desktop/share/classes/sun/font/SunFontManager.java
diffstat 3 files changed, 73 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.desktop/share/classes/sun/font/Font2D.java	Wed Dec 04 17:42:18 2019 -0800
+++ b/src/java.desktop/share/classes/sun/font/Font2D.java	Thu Dec 05 13:24:52 2019 -0800
@@ -30,8 +30,10 @@
 import java.awt.geom.AffineTransform;
 import java.lang.ref.Reference;
 import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.Locale;
+import java.util.Set;
 
 public abstract class Font2D {
 
@@ -105,7 +107,22 @@
      * This pre-supposes that a FontStrike is a shareable object, which
      * it should.
      */
-    protected Reference<FontStrike> lastFontStrike = new SoftReference<>(null);
+    protected Reference<FontStrike> lastFontStrike = new WeakReference<>(null);
+
+    /*
+     * if useWeak is true, proactively clear the cache after this
+     * many strikes are present. 0 means leave it alone.
+     */
+    private int strikeCacheMax = 0;
+    /*
+     * Whether to use weak refs for this font, even if soft refs is the default.
+     */
+    private boolean useWeak;
+
+    void setUseWeakRefs(boolean weak, int maxStrikes) {
+        this.useWeak = weak;
+        this.strikeCacheMax = weak && maxStrikes > 0 ? maxStrikes : 0;
+    }
 
     /*
      * POSSIBLE OPTIMISATION:
@@ -304,6 +321,15 @@
         return getStrike(desc, false);
     }
 
+    void updateLastStrikeRef(FontStrike strike) {
+        lastFontStrike.clear();
+        if (useWeak) {
+            lastFontStrike = new WeakReference<>(strike);
+        } else {
+            lastFontStrike = new SoftReference<>(strike);
+        }
+    }
+
     FontStrike getStrike(FontStrikeDesc desc) {
         return getStrike(desc, true);
     }
@@ -324,15 +350,13 @@
          */
         FontStrike strike = lastFontStrike.get();
         if (strike != null && desc.equals(strike.desc)) {
-            //strike.lastlookupTime = System.currentTimeMillis();
             return strike;
         } else {
             Reference<FontStrike> strikeRef = strikeCache.get(desc);
             if (strikeRef != null) {
                 strike = strikeRef.get();
                 if (strike != null) {
-                    //strike.lastlookupTime = System.currentTimeMillis();
-                    lastFontStrike = new SoftReference<>(strike);
+                    updateLastStrikeRef(strike);
                     StrikeCache.refStrike(strike);
                     return strike;
                 }
@@ -366,31 +390,21 @@
              * which is what we want for what is likely a transient strike.
              */
             int txType = desc.glyphTx.getType();
-            if (txType == AffineTransform.TYPE_GENERAL_TRANSFORM ||
+            if (useWeak ||
+                txType == AffineTransform.TYPE_GENERAL_TRANSFORM ||
                 (txType & AffineTransform.TYPE_GENERAL_ROTATION) != 0 &&
                 strikeCache.size() > 10) {
                 strikeRef = StrikeCache.getStrikeRef(strike, true);
             } else {
-                strikeRef = StrikeCache.getStrikeRef(strike);
+                strikeRef = StrikeCache.getStrikeRef(strike, useWeak);
             }
             strikeCache.put(desc, strikeRef);
-            //strike.lastlookupTime = System.currentTimeMillis();
-            lastFontStrike = new SoftReference<>(strike);
+            updateLastStrikeRef(strike);
             StrikeCache.refStrike(strike);
             return strike;
         }
     }
 
-    void removeFromCache(FontStrikeDesc desc) {
-        Reference<FontStrike> ref = strikeCache.get(desc);
-        if (ref != null) {
-            Object o = ref.get();
-            if (o == null) {
-                strikeCache.remove(desc);
-            }
-        }
-    }
-
     /**
      * The length of the metrics array must be >= 8.  This method will
      * store the following elements in that array before returning:
--- a/src/java.desktop/share/classes/sun/font/FontStrikeDisposer.java	Wed Dec 04 17:42:18 2019 -0800
+++ b/src/java.desktop/share/classes/sun/font/FontStrikeDisposer.java	Thu Dec 05 13:24:52 2019 -0800
@@ -25,6 +25,8 @@
 
 package sun.font;
 
+import java.lang.ref.Reference;
+import java.util.concurrent.ConcurrentHashMap;
 import sun.java2d.Disposer;
 import sun.java2d.DisposerRecord;
 
@@ -53,7 +55,7 @@
 class FontStrikeDisposer
     implements DisposerRecord, Disposer.PollDisposable {
 
-    Font2D font2D;
+    ConcurrentHashMap<FontStrikeDesc, Reference<FontStrike>> strikeCache;
     FontStrikeDesc desc;
     long[] longGlyphImages;
     int [] intGlyphImages;
@@ -65,7 +67,7 @@
 
     public FontStrikeDisposer(Font2D font2D, FontStrikeDesc desc,
                               long pContext, int[] images) {
-        this.font2D = font2D;
+        this.strikeCache = font2D.strikeCache;
         this.desc = desc;
         this.pScalerContext = pContext;
         this.intGlyphImages = images;
@@ -73,7 +75,7 @@
 
     public FontStrikeDisposer(Font2D font2D, FontStrikeDesc desc,
                               long pContext, long[] images) {
-        this.font2D = font2D;
+        this.strikeCache = font2D.strikeCache;
         this.desc = desc;
         this.pScalerContext = pContext;
         this.longGlyphImages = images;
@@ -81,20 +83,26 @@
 
     public FontStrikeDisposer(Font2D font2D, FontStrikeDesc desc,
                               long pContext) {
-        this.font2D = font2D;
+        this.strikeCache = font2D.strikeCache;
         this.desc = desc;
         this.pScalerContext = pContext;
     }
 
     public FontStrikeDisposer(Font2D font2D, FontStrikeDesc desc) {
-        this.font2D = font2D;
+        this.strikeCache = font2D.strikeCache;
         this.desc = desc;
         this.comp = true;
     }
 
     public synchronized void dispose() {
         if (!disposed) {
-            font2D.removeFromCache(desc);
+            Reference<FontStrike> ref = strikeCache.get(desc);
+            if (ref != null) {
+                Object o = ref.get();
+                if (o == null) {
+                    strikeCache.remove(desc);
+                }
+            }
             StrikeCache.disposeStrike(this);
             disposed = true;
         }
--- a/src/java.desktop/share/classes/sun/font/SunFontManager.java	Wed Dec 04 17:42:18 2019 -0800
+++ b/src/java.desktop/share/classes/sun/font/SunFontManager.java	Thu Dec 05 13:24:52 2019 -0800
@@ -256,6 +256,13 @@
         return t1Filter;
     }
 
+    /* After we reach MAXSOFTREFCNT, use weak refs for created fonts.
+     * This means that a small number of created fonts as used in a UI app
+     * will not be eagerly collected, but an app that create many will
+     * have them collected more frequently to reclaim storage.
+     */
+    private static int maxSoftRefCnt = 10;
+
     static {
 
         java.security.AccessController.doPrivileged(
@@ -280,6 +287,9 @@
                    System.getProperty("java.home","") + File.separator + "lib";
                jreFontDirName = jreLibDirName + File.separator + "fonts";
 
+                maxSoftRefCnt =
+                    Integer.getInteger("sun.java2d.font.maxSoftRefs", 10);
+
                return null;
            }
         });
@@ -2283,6 +2293,8 @@
     Thread fileCloser = null;
     Vector<File> tmpFontFiles = null;
 
+    private int createdFontCount = 0;
+
     public Font2D[] createFont2D(File fontFile, int fontFormat, boolean all,
                                  boolean isCopy, CreatedFontTracker tracker)
     throws FontFormatException {
@@ -2293,10 +2305,21 @@
         FileFont font2D = null;
         final File fFile = fontFile;
         final CreatedFontTracker _tracker = tracker;
+        boolean weakRefs = false;
+        int maxStrikes = 0;
+        synchronized (this) {
+            if (createdFontCount < maxSoftRefCnt) {
+                createdFontCount++;
+            } else {
+                  weakRefs = true;
+                      maxStrikes = 10;
+            }
+        }
         try {
             switch (fontFormat) {
             case Font.TRUETYPE_FONT:
                 font2D = new TrueTypeFont(fontFilePath, null, 0, true);
+                font2D.setUseWeakRefs(weakRefs, maxStrikes);
                 fList.add(font2D);
                 if (!all) {
                     break;
@@ -2304,11 +2327,14 @@
                 cnt = ((TrueTypeFont)font2D).getFontCount();
                 int index = 1;
                 while (index < cnt) {
-                    fList.add(new TrueTypeFont(fontFilePath, null, index++, true));
+                    font2D = new TrueTypeFont(fontFilePath, null, index++, true);
+                    font2D.setUseWeakRefs(weakRefs, maxStrikes);
+                    fList.add(font2D);
                 }
                 break;
             case Font.TYPE1_FONT:
                 font2D = new Type1Font(fontFilePath, null, isCopy);
+                font2D.setUseWeakRefs(weakRefs, maxStrikes);
                 fList.add(font2D);
                 break;
             default: