sun.misc.Unsafe的各种神技Unsafe

原创
2018/05/24 14:05
阅读数 1.2K

1. sun.misc.Unsafe包

 

Unsafe类在jdk 源码的多个类中用到,这个类的提供了一些绕开JVM的更底层功能,基于它的实现可以提高效率。但是,它是一把双刃剑:正如它的名字所预示的那样,它是Unsafe的,它所分配的内存需要手动free(不被GC回收)。Unsafe类,提供了JNI某些功能的简单替代:确保高效性的同时,使事情变得更简单。

这篇文章主要是以下文章的整理、翻译。

http://mishadoff.com/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/

1. Unsafe API的大部分方法都是native实现,它由105个方法组成,主要包括以下几类:

(1)Info相关。主要返回某些低级别的内存信息:addressSize(), pageSize()

(2)Objects相关。主要提供Object和它的域操纵方法:allocateInstance(),objectFieldOffset()

(3)Class相关。主要提供Class和它的静态域操纵方法:staticFieldOffset(),defineClass(),defineAnonymousClass(),ensureClassInitialized()

(4)Arrays相关。数组操纵方法:arrayBaseOffset(),arrayIndexScale()

(5)Synchronization相关。主要提供低级别同步原语(如基于CPU的CAS(Compare-And-Swap)原语):monitorEnter(),tryMonitorEnter(),monitorExit(),compareAndSwapInt(),putOrderedInt()

(6)Memory相关。直接内存访问方法(绕过JVM堆直接操纵本地内存):allocateMemory(),copyMemory(),freeMemory(),getAddress(),getInt(),putInt()

 

2. 实例化私有类

 正常情况下没法实例化一个私有构造函数的类,但是Unsafe可以做到。

import java.lang.reflect.Field;  
  
import sun.misc.Unsafe;  
  
public class UnsafePlayer {  
      
    public static void main(String[] args) throws Exception {    
    //通过反射实例化Unsafe  
        Field f = Unsafe.class.getDeclaredField("theUnsafe");  
        f.setAccessible(true);    
        Unsafe unsafe = (Unsafe) f.get(null);    
    
        //实例化Player  
        Player player = (Player) unsafe.allocateInstance(Player.class);   
        player.setName("li lei");  
        System.out.println(player.getName());  
          
    }    
}    
    
class Player{   
      
    private String name;  
      
    private Player(){}  
      
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
}  

 

3. cas原子级操作&&通过内存偏移地址修改变量值

import java.lang.reflect.Field;  
  
import sun.misc.Unsafe;  
  
public class UnsafePlayer {  
      
    public static void main(String[] args) throws Exception {    
        //通过反射实例化Unsafe  
        Field f = Unsafe.class.getDeclaredField("theUnsafe");  
        f.setAccessible(true);    
        Unsafe unsafe = (Unsafe) f.get(null);    
    
        //实例化Player  
        Player player = (Player) unsafe.allocateInstance(Player.class);   
        player.setAge(18);  
        player.setName("li lei");  
        for(Field field:Player.class.getDeclaredFields()){  
            System.out.println(field.getName()+":对应的内存偏移地址"+unsafe.objectFieldOffset(field));  
        }  
        System.out.println("-------------------");  
          
        int ageOffset= 8;  
        //修改内存偏移地址为8的值(age),返回true,说明通过内存偏移地址修改age的值成功  
        System.out.println(unsafe.compareAndSwapInt(player, ageOffset, 18, 20));  
        System.out.println("age修改后的值:"+player.getAge());  
        System.out.println("-------------------");  
          
        //修改内存偏移地址为8的值,但是修改后不保证立马能被其他的线程看到。  
        unsafe.putOrderedInt(player, 8, 33);  
        System.out.println("age修改后的值:"+player.getAge());  
        System.out.println("-------------------");  
          
        //修改内存偏移地址为12的值,volatile修饰,修改能立马对其他线程可见  
        unsafe.putObjectVolatile(player, 12, "han mei");  
        System.out.println("name修改后的值:"+unsafe.getObjectVolatile(player, 12));  
    }    
}    
    
class Player{   
      
    private int age;  
      
    private String name;  
      
    private Player(){}  
      
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
  
    public int getAge() {  
        return age;  
    }  
  
    public void setAge(int age) {  
        this.age = age;  
    }  
      
}  


3. 创建超级数组

    java中数组的最大长度为Integer.MAX_VALUE,正常情况下如果想创建一个大于Integer.MAX_VALUE的数组是做不到的,但是Unsafe可以,通过对内存进行直接分配实现。

import java.lang.reflect.Field;  
  
import sun.misc.Unsafe;  
  
public class SuperArray {  
          
    public static void main(String[] arg) throws Exception{  
          
        //通过反射实例化Unsafe  
        Field f = Unsafe.class.getDeclaredField("theUnsafe");  
        f.setAccessible(true);    
         Unsafe unsafe = (Unsafe) f.get(null);    
          
         //只要内存够大,可以把这个调大,大于Integer.MAX_VALUE  
         long size = (long)Integer.MAX_VALUE/2 ;    
         long addr = unsafe.allocateMemory(size);  
         System.out.println("unsafe address :"+addr);  
           
         for (int i = 0; i < size; i++) {    
             unsafe.putByte(addr+i, (byte)6);   
             if(unsafe.getByte(addr+i) !=6){  
                 System.out.println("failed at offset");  
             }   
        }    
    }  
      
}  

把size调大,size = (long)Integer.MAX_VALUE*2,错误信息如下。

unsafe address :15817328  
#  
# A fatal error has been detected by the Java Runtime Environment:  
#  
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x61cc0350, pid=31240, tid=31324  
#  
# JRE version: Java(TM) SE Runtime Environment (8.0_45-b14) (build 1.8.0_45-b14)  
# Java VM: Java HotSpot(TM) Client VM (25.45-b02 mixed mode windows-x86 )  
# Problematic frame:  
# V[thread 30484 also had an error]  
  [jvm.dll+0x40350]  
#  
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows  
#  
# An error report file with more information is saved as:  
# C:\welab\workspace\Person\hs_err_pid31240.log  


 

4.线程挂起与恢复

 将一个线程进行挂起是通过park方法实现的,调用 park后,线程将一直阻塞直到超时或者中断等条件出现。unpark可以终止一个挂起的线程,使其恢复正常。整个并发框架中对线程的挂起操作被封装在 LockSupport类中,LockSupport类中有各种版本pack方法,但最终都调用了Unsafe.park()方法。

public class LockSupport {    
  
    /** 
     * 恢复阻塞线程 
     */  
    public static void unpark(Thread thread) {    
        if (thread != null)    
            unsafe.unpark(thread);    
    }    
    
    /** 
     * 一直阻塞当前线程,调用Unsafe.park()方法 
     */  
    public static void park(Object blocker) {    
        Thread t = Thread.currentThread();    
        setBlocker(t, blocker);    
        unsafe.park(false, 0L);    
        setBlocker(t, null);    
    }    
    /** 
     * 阻塞当前线程nanos纳秒 
     */  
    public static void parkNanos(Object blocker, long nanos) {    
        if (nanos > 0) {    
            Thread t = Thread.currentThread();    
            setBlocker(t, blocker);    
            unsafe.park(false, nanos);    
            setBlocker(t, null);    
        }    
    }    
    
    public static void parkUntil(Object blocker, long deadline) {    
        Thread t = Thread.currentThread();    
        setBlocker(t, blocker);    
        unsafe.park(true, deadline);    
        setBlocker(t, null);    
    }    
    
    /** 
     * 一直阻塞当前线程 
     */  
    public static void park() {    
        unsafe.park(false, 0L);    
    }    
     
   /** 
     * 阻塞当前线程nanos纳秒 
     */  
    public static void parkNanos(long nanos) {    
        if (nanos > 0)    
            unsafe.park(false, nanos);    
    }    
    
    public static void parkUntil(long deadline) {    
        unsafe.park(true, deadline);    
    }    
}  

  

最后看下阻塞和恢复的例子

import java.util.concurrent.locks.LockSupport;  
  
public class Lock {  
     
    public static void main(String[] args) throws InterruptedException {  
          
        ThreadPark threadPark = new ThreadPark();  
        threadPark.start();  
        ThreadUnPark threadUnPark = new ThreadUnPark(threadPark);  
        threadUnPark.start();  
        //等待threadUnPark执行成功  
        threadUnPark.join();  
        System.out.println("运行成功....");  
    }  
      
      
  static  class ThreadPark extends Thread{  
        
       public void run(){  
            System.out.println(Thread.currentThread() +"我将被阻塞在这了60s....");  
            //阻塞60s,单位纳秒  1s = 1000000000  
            LockSupport.parkNanos(1000000000l*60);  
              
            System.out.println(Thread.currentThread() +"我被恢复正常了....");  
       }  
   }  
     
  static  class ThreadUnPark extends Thread{  
      
       public Thread thread = null;  
         
       public ThreadUnPark(Thread thread){  
           this.thread = thread;  
       }  
       public void run(){  
             
            System.out.println("提前恢复阻塞线程ThreadPark");  
            //恢复阻塞线程  
            LockSupport.unpark(thread);  
          
       }  
   }  
}  

参考:https://blog.csdn.net/dfdsggdgg/article/details/51543545

http://blog.csdn.net/fenglibing/article/details/17138079 

https://www.cnblogs.com/suxuan/p/4948608.html

Java 9中将移除 Sun.misc.Unsafe,请谨慎使用。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
5 收藏
0
分享
返回顶部
顶部