Skip to main content

JSR 292 Goodness: Almost static final field.

Posted by forax on December 17, 2011 at 10:20 AM PST

Sometimes I want a express that a static field is unlikely to change, so the VM should consider it has a constant, but it may changed. And because we all live in a threaded world, if the static field is changed I want that all threads that want to read the field value to notice that the value has changed like a volatile field. Take by example the default Locale that you can obtain using Locale.getDefault(), it's unlikely that this value will change, but it may change, there is a Locale.setDefault().

The idea is that default Locale is mostly a constant, so when a code that call getDefault is JITed, the VM should consider it as a constant i.e. the JIT should replace the call to getDefault() by a direct reference to default Locale object. And if a code calls setDefault(), the VM should drop the JITed code and re-optimize it later.

Suppose that you have a magic class named AlmostFinalValue, a value wrapped in this class acts as a constant for the VM, so a code that call getDefault will be replaced by a constant by the VM. And if setValue() is called, the VM will deoptimize the code that use getDefault if the code was JITed and change the value of the default locale. If the code still use getDefault, it will be re-optimized by with the new constant.
Note that this deopt/reopt takes time so it's not a good idea to use an AlmostFinalValue for storing a value which changes frequently.

public class Locale {
  ...
  private static final AlmostFinalValue<Locale> DEFAULT_LOCALE =
      new AlmostFinalValue<Locale>() {
        @Override
        protected Locale initialValue() {
          return new Locale("default");
        }
      };
  private static final MethodHandle DEFAULT_LOCALE_GETTER =
      DEFAULT_LOCALE.createGetter();
 
  public static Locale getDefault() {
    try {
      return (Locale)(Object)DEFAULT_LOCALE_GETTER.invokeExact();
    } catch (Throwable e) {
      throw new AssertionError(e.getMessage(), e);
    }
  }
 
  public static void setDefault(Locale locale) {
    DEFAULT_LOCALE.setValue(locale);
  }
}

 

How AlmostFinalValue works ?

All modern VMs, V8-cranksaft (Javascript/Google), IonMonkey (Javascript/Mozilla), Hostspot (Java/Oracle), J9 (Java/IBM) are able to de-optimize a JITed code if an optimistic assumption doesn't hold anymore.
In Java, we go a little bit further, JSR 292, foolishly you may say, gives access to VM optimization/deoptimization to the mass* by providing a silver bullet named the SwitchPoint.

A SwitchPoint can be seen as a volatile boolean which is true by default and that can be switched off once. On a SwitchPoint, you can create a guard, an 'if' statement seen as a method handle** that takes two method handles as parameter and execute the former if the boolean is true or the later otherwise. The JIT optimizes a SwitchPoint by considering that the boolean value is unlinkely to change so it will remove the guard to always call the first method handle and if the SwitchPoint is switch off, the VM will deoptimize the JITed code. In fact, in Hotspot, this is done lazily the code is just marked as must be dropped when it will be called. So later, when code will be called again, the VM will call the second method handle. The second method is the slow path, the path which is infrequenly called when there is an update, it get the new value, see it as a constant method handle and protect it with a new SwitchPoint. Then this new method handle is installed as the new target of a mutable method handle (a MutableCallSite + a dynamic invoker in fact). So the new constant is installed and will be called when the DEFAULT_LOCAL_GETTER will be called. The method hande DEFAULT_LOCAL_GETTER is called using invokeExact which calls the method handle with no runtime conversion. As a game, I will offer a free beer at FOSDEM or Devoxx France to the first that will explain why you need the two casts in front of invokeExact ***.

The full code is here, and works well with latest jdk8 and jdk7u2. With jdk7, the SwitchPoint was not fully optimized so the VM will fail to transform the default local value to a constant.

* In fact, JSR 292 was intended to be used by developers of dynamic language runtime (Jython, JRuby, Groovy, etc) but when you provide a golden hammer, everybody will want to use it.
** A MethodHandle is runtime typed function pointers.
*** People that have written the JSR 292 spec can't participate, sorry John, Dan and Fredrik :)

Related Topics >>

Comments

&nbsp;Wouldn't something like this also work? (sorry for the ...

Wouldn't something like this also work? (sorry for the pastebin link, but the comment area doesn't like generics).

Or there is some hidden overhead that I'm not seeing?

No, even if the field named 'getter' is declared final, you ...

No, even if the field named 'getter' is declared final, you can change it by reflection so the VM will not replace it by a constant.

cheers,
Rémi
 

Without the double cast, &nbsp;invokeExact would fail ...

Without the double cast, invokeExact would fail with:

Caused by: java.lang.invoke.WrongMethodTypeException: ()Ljava/lang/Object; cannot be called as ()Ljava/util/Locale;

The explanation reasoned/found from the javadoc of MethodHandle#invokeExact(Object[]):

"The symbolic type descriptor at the call site of {@code invokeExact} must exactly match this method handle's {@link #type type}. No conversions are allowed on arguments or return values."

"@throws WrongMethodTypeException if the target's type is not identical with the caller's symbolic type descriptor"

So the first cast is to satisfy the invokeExact API to avoid the WrongMethodTypeExcepiton, whereas the second cast is to subsequently narrow the Object to the known returned object type which is Locale in this case.

Seems strange to me, however, why the underlying invokeExact API has such odd requirement in the first place.

Regards,

Hanson

Me trying to make a long story short: We want function ...

Me trying to make a long story short:
We want function pointesr but because we are in Java, we want safe function pointer.
There are two ways to implement that, the first is to have a special kind of object with a function signature, this is what the delegate are in C#, in the VM recognize these objects you can allow contravariance of the parameters and covariance of the return type i.e the classical relation between the caller signature et compile time and the callee signature. But it's hard to write a simple vtable with such beasts.
Or you can have one signature (like void* in C) that represent a kind of generic function pointer, a MethodHandle in Java parlance, but because you do want type safety at runtime, you have to do a signature check at runtime. Testing covariant signatures at runtime is hard to do fast, so the idea is that if you require to have exactly the same signature, you can implement that check as a pointer check. So invokeExact() is a fast safe call.
But the API also provide another way to call a method handle, nvoke() which let you call a method handle and will allow more conversions (even boxing and varargs) but with no performance guarantee. Note that here, because the method handle is constant, the conversions implied by invoke() is constant too, so an invoke() should be as fast as an invokeExact() if the VM is in good mood :)

Rémi
 

The double cast is because incokeExact returns Object (the ...

The double cast is because incokeExact returns Object (the call site was initialzied with MethodType.methodType( Object.class )) and method handle's type must match the descriptor at the call site - so caller must declare that he expects the Object and cannot cast to Locale directly. Call to incoke() will perform conversion on return value so only one cast would be needed in that case.

In case I am right & first, can you ship the beer to Poland? :)

Thanks, Tomek

Tomek, bravo! your right! and yes, invoke will work here, ...

Tomek, bravo! your right!
and yes, invoke will work here, another idea is to provide the class (Locale.class) to the constructor of the CallSite to initialize it with the correct class.

About the beer, let me know if one day you want to visit Paris, I will be there with a beer :)

Rémi
 

Your link just links me back to this blog article. Guessing ...

Your link just links me back to this blog article. Guessing you meant to link here?

Your link just links me back to this blog article. Guessing ...

thanks, fixed.

Rémi
 

&nbsp;Hi Remi, What is the purpose of creating a local ...

Hi Remi,

What is the purpose of creating a local "lock" object for use in a synchronized block in the AmostFinalCallSite ctor, when such lock can be seen by one and only one thread at a time ? Is it for the purpose of safe publication ?
Thanks,
Hanson
AlmostFinalCallSite(AlmostFinalValue<V> volatileFinalValue) {
super(MethodType.methodType(Object.class));
Object lock = new Object();
MethodHandle fallback = FALLBACK.bindTo(this);
synchronized(lock) {
value = NONE;
switchPoint = new SwitchPoint();
setTarget(fallback);
}
this.volatileFinalValue = volatileFinalValue;
this.lock = lock;
this.fallback = fallback;
}

Yes, it's for avoiding an unsafe publication. I beleive ...

Yes, it's for avoiding an unsafe publication.
I beleive than I don't need this here because I only use this constructor in the boostrap method and I think that the VM guarantee that a callsite is fully initialized before being used.
The JSR 292 spec says "If several threads simultaneously execute a bootstrap method for a single dynamic call site, the JVM must choose one CallSite object and install it visibly to all threads."
But it was not clear enough for me. I wanted to ask John Rose before publishing that code and forget to do it.

Rémi

&nbsp;So I'm allowed to submit an answer? :)

So I'm allowed to submit an answer? :)

Too easy for you :) but I will be happy to offer you a beer ...

Too easy for you :)
but I will be happy to offer you a beer during the next JVM Summit.

Rémi