2016-10-15 3 views
6

Ich schreibe einige FFI-Code in Java unter Verwendung eines von zwei Implementierungen einer Klasse kompiliert, die starke Nutzung von sun.misc.Unsafe macht.Schreiben Java-Code, der

In Java 9 wird diese Klasse nicht mehr zugegriffen werden, und jdk.unsupported.Unsafe werden wird. Ich möchte meinen Code schreiben, so dass es jetzt funktioniert, setzt aber 9.

Was in Java arbeiten, ist die am wenigsten hacky Weg, dies zu tun? Ich würde Binärkompatibilität bevorzugen, aber Quellkompatibilität ist auch in Ordnung.

Edit: Ich bin 100% bei der Verwendung von Reflexion nicht in Ordnung - oder sogar virtuellen Versands - jedes Mal, wenn ein Verfahren auf Unsafe genannt wird. Die meisten dieser Methoden kompilieren zu einer einzigen Maschinenanweisung. Daher ist Leistung wirklich wichtig. Es ist in Ordnung, Wrapper zu haben - aber nur, wenn ich sicher sein kann, dass der JIT sie jedes Mal inline bringt.

Mein aktueller Plan ist es, eine geeignete Klasse zur Laufzeit zu laden.

+0

Es wird "nicht unterstützt" ... – r1verside

+0

@ r1verside Ich weiß, aber es ist fast sicher, nicht wegzugehen, weil so viel Code (einschließlich neuer Entwicklung!) Davon abhängt. – Demi

+0

Vielleicht ist es an der Zeit, das zu tun, was Sie vor langer Zeit hätten tun sollen: Korrigieren Sie Ihren Code (Sie verwenden also keinen knorrigen alten Sonnencode). – Bohemian

Antwort

2

Eine Möglichkeit: Man könnte einen kleinen Shim Helfer-Schnittstelle für die Methode (n) auf Unsafe hat, dass Sie mit zwei Implementierungen Zugang zu müssen: ein für sun.misc.Unsafe und einen für das neue jdk.unsupported.Unsafe. Beide Klassen könnten als binäre Ressourcen in Ihrer JAR gespeichert werden, und während der Klasseninitialisierung (dh in einem static Block) könnten Sie einen neuen ClassLoader erstellen und die Shim-Klasse laden. Dies würde Ihnen während der Klasseninitialisierung einen gewissen Overhead geben, aber zur Laufzeit gibt es keine Reflektion, sondern nur einen virtuellen Methodenversand, den das JIT inline einbinden kann, solange Sie nur die eine Implementierungsklasse laden.

Wenn Sie eine Bibliothekabhängigkeit einführen können, wird die FastClass von cglib das für Sie mit viel weniger Aufwand tun.

Wie immer bei Low-Level-Performance-Hacks benötigen Sie einige Daten. Erstellen Sie einen JMH-Test-Kabelbaum und stellen Sie sicher, dass der Reflektions-Overhead wirklich nicht tolerierbar ist und dass der JIT Ihre Lösung wirklich inline - das ist der einzige Weg, um sicher zu sein.

0

Eine Möglichkeit wäre, eine Klasse zu schreiben, die entweder Implementierung wickelt und verwendet eine Hilfsmethode die richtige Umsetzung zu erhalten. Sie könnten System.getProperties(). GetProperty ("java.version") überprüfen, um zu wissen, ob Sie in JDK 1.8 oder 1.9 sind, oder Sie können einfach versuchen/fangen, um die Klasse wie ich zu bekommen.

Leider gibt es keine Schnittstelle zu implementieren, so dass Sie es wie ein einfaches Objekt verwenden. Sie müssen jede Methode umbrechen. Vielleicht gibt es einen allgemeineren Weg, dies zu tun.

Beispiel Beginn der Umsetzung

/** 
* A wrapper to a sun.misc.Unsafe or jdk.unsupported.Unsafe object. 
*/ 
public class MyUnsafe { 

    // the implementing class (sun.misc.Unsafe or jdk.unsupported.Unsafe) 
    private Class<?> unsafeCls; 

    // an instance of the implementing class 
    private Object unsafeObj; 

    // constructor 
    public MyUnsafe() throws InstantiationException, IllegalAccessException { 
     unsafeCls = getImplClass(); 
     unsafeObj = unsafeCls.newInstance(); 
    } 

    // get the implementing class 
    private Class<?> getImplClass() { 
     Class<?> implClass = null; 
     try { 
      // JDK 1.8 and earlier 
      implClass = Class.forName("sun.misc.Unsafe"); 
     } 
     catch (ClassNotFoundException e1) { 
      try { 
       // JDK 1.9 and later 
       implClass = Class.forName("jdk.unsupported.Unsafe"); 
      } 
      catch (ClassNotFoundException e2) { 
       // TODO - something that wraps both e1 and e2 
       throw new RuntimeException(e2); 
      } 
     } 
     return implClass; 
    } 

    // Wrap methods 

    // for example 
    public Object getObject(Object obj, long offset) throws Exception { 
     Method mtd = unsafeCls.getMethod("getObject", new Class<?>[] { Object.class, long.class }); 
     return mtd.invoke(unsafeObj, new Object[] { obj, offset }); 
    } 

    // and so on... 
} 
+1

Das wird nicht funktionieren. Der Reflektionsaufwand für jeden Methodenaufruf wird die Leistung beeinträchtigen. Ich brauche etwas, das es dem JIT ermöglicht, die Wrapper zu inline zu schreiben - je nach der JDK-Version, je nach der Art des Ladens der einen oder anderen Klasse. – Demi

+0

Sind Sie sich über die Leistung sicher? Meiner Erfahrung nach ist die Reflektion nicht viel langsamer als die übliche interne Überprüfung durch Java. – pmcevoy12

0

Die Low-Tech-Lösung wäre natürlich, einfach zu dokumentieren, dass Ihre Bibliothek mit Java 9 inkompatibel ist, und einen separaten Versionszweig zu veröffentlichen, der auf Java 9 (aber nicht auf früheren Versionen) funktioniert. Dies schiebt das Problem auf Ihre Kunden, aber das ist wahrscheinlich gut genug für die überwiegende Mehrheit der realen Anwendungsfälle.

Verwandte Themen