Sharing Secrets
One interesting issue when writing a runtime class library for Java is how to give implementation packages, whether they be in gnu.* or com.sun.*, specialised access to the core runtime classes like those in java.lang. We ran across this problem again recently with GNU Classpath when trying to write CPStringBuilder. This is a StringBuilder variant that differs in how it utilises its internal character array. StringBuffer and StringBuilder both maintain their own character array throughout their life, creating a new larger one and copying when appropriate. When toString() or substring(int,int) is called, the new String object is given a copy of the array.
CPStringBuilder instead optimises for the frequent cases where a StringBuilder is created, used to build a String and then discarded after toString() is called. It does so by handing a reference to the character array to the new String object on creation. Once this is done, it flags internally that the array has been used and creates a new one if any further writes are requested. Thus, using CPStringBuilder should always be one copy more efficient than using StringBuilder or StringBuffer (possibly even creating just a single array if the final string is within the initial capacity) and we now use it internally when the builder does not need to be thread-safe.
The problem with implementing this is that it requires passing the array to the constructor of the String object. There is no such constructor in the public API for String, although Classpath has had a package-private one for sometime (GCJ and String itself already use it). But how do we access this from the gnu.java.lang package? Our current method is to use reflection, and thus we have VMCPStringBuilder so VMs can optimise this natively.
When looking through OpenJDK for the VM project, I noticed that they have a rather interesting solution to this. This is encapsulated in sun.misc.SharedSecrets. This class provides access to instances of a number of public interfaces, such as sun.misc.JavaLangAccess. The actual implementations are provided as inner classes in the appropriate package e.g. java.lang, where it has access to the private and package-private variables and methods within. For instance, JavaLangAccess provides access to the constant pool for a particular class.
The only noticeable negative side effect of this is that any external class may also call these methods. For example:
import sun.misc.SharedSecrets;
public class TestSecrets
{
public static void main(String[] args)
{
System.out.println(SharedSecrets.getJavaLangAccess().getConstantPool(String.class));
}
}
whereas the equivalent for GNU Classpath:
import gnu.java.lang.VMCPStringBuilder;
public class TestCPSecrets
{
public static void main(String[] args)
{
System.out.println(VMCPStringBuilder.toString(new char[]{'H','e','l','l','o'},0,5));
}
}
fails to compile because VMCPStringBuilder is package-private (although the equivalent is possible by using CPStringBuilder in this case). As a result, it becomes slightly more important to ensure that the possible damage from using these classes is limited.
That said, this may be an interesting and more efficient method for us to look into for GNU Classpath.
May 28th, 2008 at 5:33 am
If your code requires access to java.lang internals, “my” code might need access, too. So instead of messing with the language, why not making the constructors and methods public?
May 29th, 2008 at 10:53 pm
Using features of the language (like package privacy and inner classes) and of the class library (reflection) are hardly what I’d call ‘messing’ with it.
The ideas here are really no different to ones you might employ on any large body of Java code. Once you start having multiple packages, there will occasionally be the odd method where you need to provide internal access from package to package within your library but not to external consumers. This applies for the class library (certainly one of the biggest bodies of Java code) as much as for anything else. On the other hand, would you really want any application altering internal parts of the VM? With the constant pool that’s what we’re talking about to a large extent. Fortunately, sun.misc.ConstantPool seems to be purely read-only.
With 1.7, we will hopefully see the module system and superpackages which will make for cleaner solutions for these issues.