JDK-4812194 : Suggest ResourceBundle.loadBundle check for .class before calling Class.forName
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.util:i18n
  • Affected Version: 1.4.1_01
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: linux_redhat_7.2
  • CPU: x86
  • Submitted: 2003-02-03
  • Updated: 2004-09-15
  • Resolved: 2004-09-15
Related Reports
Duplicate :  
Relates :  
Description
During startup of an application that uses *.properties resource bundles heavily (e.g. NetBeans / Sun ONE Studio IDE), there is some overhead caused by ResourceBundle's search algorithm. Specifically, loadBundle (used to physically try loading a bundle from a given loader using a given base name + locale) first tries Class.forName to load .class resources, then if that fails (typically with ClassNotFoundException), then tries looking for a .properties file.

The problem is that an exception is being used for control flow - a perfectly normal situation results in potentially many exceptions being created and then discarded, for which the JVM is poorly optimized. ClassLoader currently does not provide any API for loading a class and returning null on failure.

Suggest a tiny optimization that would be helpful: loadBundle should first check for the existence of the .class file:

String classFile = bundleName.replace('.', '/') + ".class";
// similarly for loader == null of course:
if (loader.getResource(classFile) != null) {
    // try Class.forName as before
}
// continue trying .properties

This would avoid the CNFE in the primary control flow.

One caveat: I am not sure if the ClassLoader specification requires that a loader supply a foo/Bar.class resource for every foo.Bar class it is capable of loading. Certainly all loaders I have come across do so, but it is not clear if this is necessarily true for exotic network or dynamic loaders.

Sample test class:

---%<---
import java.net.URL;
import java.util.*;
public class RBTest {
    public static void main(String[] x) throws Exception {
        try {
            ResourceBundle.getBundle("foo.bar", Locale.getDefault(), new L());
        } catch (MissingResourceException e) {
            // ignore
        }
    }
    private static final class L extends ClassLoader {
        public Class loadClass(String n) throws ClassNotFoundException {
            System.out.println("loadClass: " + n);
            return super.loadClass(n);
        }
        public URL getResource(String n) {
            System.out.println("getResource: " + n);
            return super.getResource(n);
        }
    }
}
---%<---

Actual output:

---%<---
java version "1.4.1_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_01-b01)
Java HotSpot(TM) Client VM (build 1.4.1_01-b01, mixed mode)

loadClass: foo.bar
getResource: foo/bar.properties
loadClass: foo.bar_en
getResource: foo/bar_en.properties
loadClass: foo.bar_en_US
getResource: foo/bar_en_US.properties
---%<---

Would prefer to see e.g.:

---%<---
getResource: foo/bar.class
loadClass: foo.bar
getResource: foo/bar.properties
getResource: foo/bar_en.class
loadClass: foo.bar_en
getResource: foo/bar_en.properties
getResource: foo/bar_en_US.class
loadClass: foo.bar_en_US
getResource: foo/bar_en_US.properties
---%<---

###@###.### 2003-02-03

Comments
EVALUATION We will provide a way to avoid class lookup. Closing this RFE as a duplicate of 4303146. ###@###.### 2004-09-15
15-09-2004