Java企业教程系列

Unsafe是什么?

  Java虚拟机最大的竞争对手可能是C#语言的CLR。CLR允许编写不安全的低层次代码作为入门编程,但是这很难在JVM上实现。如果你需要用Java这样的特殊功能,你可能会被迫使用JNI,这又需要你要知道一些C,而且代码会紧耦合到一个特定平台。sun.misc.Unsafe是另一个替代在Java plarform上使用Java API进行低级编程的工具,kryo和Twitter的Storm都是基于此库。它也即将成为 public API in Java 9.

  sun.misc.Unsafe类是由核心Java类使用,因此,它的构造器是私有的,只增加一个私有的单例实例,公有的getter方法会执行一个安全检查,以避免它被公开使用:

public static Unsafe getUnsafe() {
Class cc = sun.reflect.Reflection.getCallerClass(2);
if (cc.getClassLoader() != null)
throw new SecurityException("Unsafe");
return theUnsafe;
}

该方法首先从当前线程的方法堆栈查找调用类。这个查询是由sun.reflection.Reflection另一个内部类实现,基本上向下浏览调用堆栈帧然后返回方法定义的类。这种安全检查不过可能在未来的版本中改变。

当浏览堆栈时,第一个找到的类(索引0 )是Reflection类自己,第二个(索引1)将是Unsafe类,,索引2将持有你的调用Unsafe# getUnsafe ( )的应用程序类 。然后检查其类加载器直至最上层空引用。

由于在类加载器中没有一个核心Java类,因此永远不能够直接调用此方法,因为会收到的抛出SecurityException作为回答。 (从技术上讲,你可能会迫使虚拟机使用引导类加载器通过将其添加到- Xbootclasspath上加载应用程序的类,但这需要应用程序代码之外的一些设置。 )因此,下面的测试一定会成功:

@Test(expected = SecurityException.class)
public void testSingletonGetter() throws Exception {
Unsafe.getUnsafe();
}

我们可以使用反射机制对其私有构造器进行构造,从而获得Unsafe实例:

Constructor<Unsafe> unsafeConstructor = Unsafe.class.getDeclaredConstructor();
unsafeConstructor.setAccessible(true);
Unsafe unsafe = unsafeConstructor.newInstance();

 

直接操作内存

  使用直接Unsafe分配和释放内存以及写和读。通过下面方式获得Unsafe实例:

private static Unsafe getUnsafe() throws Exception {
// Get the Unsafe object instance
Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
return (sun.misc.Unsafe) field.get(null);
}

直接从内存读写, 通过其方法allocateMemory获得内存地址,通过putAddress 或putByte 写内存即可:

public static void showBytes() {
    try {
       Unsafe unsafe = getUnsafe();
 
       // Writing to a memory - MAX VALUE Byte
       byte value = Byte.MAX_VALUE;
       long bytes = 1;
       // Allocate given memory size
       long memoryAddress = unsafe.allocateMemory(bytes);
       // Write value to the allocated memory
       unsafe.putAddress(memoryAddress, value); // or putByte
 
       // Output the value written and the memory address
       System.out.println("[Byte] Writing " + value + " under the " + memoryAddress + " address.");
 
       long readValue = unsafe.getAddress(memoryAddress); // or getByte
 
       // Output the value from
       System.out.println("[Byte] Reading " + readValue + " from the " + memoryAddress + " address.");
 
       // C style! Release the Kraken... Memory!! :)
       unsafe.freeMemory(memoryAddress);
 
    } catch (Exception e) {
       e.printStackTrace();
    }
  }

从内存直接写一个Long值:

private static void showLong() {
    try {
       Unsafe unsafe = getUnsafe();
 
       // Writing to a memory - MAX VALUE of Long
       long value = Long.MAX_VALUE;
       long bytes = Long.SIZE;
       // Allocate given memory size
       long memoryAddress = unsafe.allocateMemory(bytes);
       // Write value to the allocated memory
       unsafe.putLong(memoryAddress, value);
 
       // Output the value written and the memory address
       System.out.println("[Long] Writing " + value + " under the " + memoryAddress + " address.");
 
       // Read the value from the memory
       long readValue = unsafe.getLong(memoryAddress);
 
       // Output the value from
       System.out.println("[Long] Reading " + readValue + " from the " + memoryAddress + " address.");
 
       // C style! Release the Kraken... Memory!! :)
       unsafe.freeMemory(memoryAddress);
 
    } catch (Exception e) {
       e.printStackTrace();
    }
  }

重新分配内存:

public static void showDontFreeMemory() {
    for (int t = 0; t < 100; t++) {
       new Thread() {
           public void run() {
              System.out.println("Thread " + Thread.currentThread().getName() + " start!");
                     for (int i = 0; i < 1000000; i++) {
                            try {
                                   Unsafe unsafe = getUnsafe();
 
                                   // Writing random Long to a memory
                                   long value = new Random().nextLong();
                                   long bytes = Long.SIZE;
                                   // Allocate given memory size
                                   long memoryAddress = unsafe.allocateMemory(bytes);
                                   // Write value to the allocated memory
                                   unsafe.putLong(memoryAddress, value);
 
                                   // Read the value from the memory
                                   long readValue = unsafe.getLong(memoryAddress);
 
                                   // Always free the memory !!
                                   // ... FIXME: deallocate the memory used
 
                                } catch (Exception e) {
                                   e.printStackTrace();
                                }
                     }
 
              System.out.println("Thread " + Thread.currentThread().getName() + " stop!");
              };
 
       }.start();
    }
  }

Java性能优化要点