美文网首页
Unsafe类初探

Unsafe类初探

作者: 菜鸟蚂蚁 | 来源:发表于2018-07-21 09:37 被阅读0次

Unsafe类说明

从这个类的名字Unsafe上来说这个类就是一个不安全的类,也是不开放给用户直接使用的(当然我们还是可以通过其他一些方法用到)。

这个类在jdk源码中多个类中用到,主要作用是任意内存地址位置处读写数据,外加一下CAS操作。它的大部分操作都是绕过JVM通过JNI完成的,因此它所分配的内存需要手动free,所以是非常危险的。但是Unsafe中很多(但不是所有)方法都很有用,且有些情况下,除了使用JNI,没有其他方法弄够完成同样的事情。

至于研究它的起因,是因为我最近在看jdk8的ConcurrentHashMap,这个版本的主要函数就是用过Unsafe来完成的。

Unsafe类的调用

Unsafe类是一个单例,调用的方法为getUnsafe,如下。可以看到,虽然是可以调用,但是会有一步判断,判断是不是内部会检查该CallerClass是不是由系统类加载器BootstrapClassLoader加载。由系统类加载器加载的类调用getClassLoader()会返回null,所以要检查类是否为bootstrap加载器加载只需要检查该方法是不是返回null。

```

@CallerSensitive

public static Unsafe getUnsafe() {

    Class var0 = Reflection.getCallerClass();

    if (!VM.isSystemDomainLoader(var0.getClassLoader())) {

        throw new SecurityException("Unsafe");

    }else {

    return theUnsafe;

    }

}

```

那我们怎么能调用到它呢,有以下2中方法。

1、通过JVM参数-Xbootclasspath指定要使用的类为启动类;

```

java -Xbootclasspath/a: ${path}   // 其中path为调用Unsafe相关方法的类所在jar包路径 .

```

2、在Unsafe类中有一个成员变量theUnsafe,因此我们可以通过反射将private单例实例的accessible设置为true,然后通过Field的get方法获取,如下:

```

Field f = Unsafe.class.getDeclaredField("theUnsafe");

f.setAccessible(true);

Unsafe unsafe = (Unsafe) f.get(null);

```

不调用构造方法生成对象

利用Unsafe的allocateInstance方法,可以在未调用构造方法的情况下生成了对象。下面的例子很好的说明了这一点。

static class City {

privateString name ="";

privateintflag =0;

public City() {

    this.name ="Beijing";

    this.flag =1;

 }

@Override

public String toString(){

            return name +": "+ flag;

      }

  }

public static void main(String[] args) throwsException {

    // 通过反射得到theUnsafe对应的Field对象

    Field field = Unsafe.class.getDeclaredField("theUnsafe");

    // 设置该Field为可访问

    field.setAccessible(true);

    // 通过Field得到该Field对应的具体对象,传入null是因为该Field为static的

    Unsafe unsafe = (Unsafe) field.get(null);

      City city = (City) unsafe.allocateInstance(City.class);

    System.out.println(city);//dont invoke constructor, print null: 0

    City anotherCity =newCity();

    System.out.println(anotherCity);//print Beijing: 1

  }

内存修改

我们看一下下面的代码,在正常的情况下sizeValidate始终范围false,但是通过计算MAX_SIZE的位移,将其进行修改之后,就会返回true。

static class Validation{

    private int MAX_SIZE =10;

    public boolean sizeValidate(){

        return 20< MAX_SIZE;

      }

  }

public static void main(String[] args) throwsException{

// 通过反射得到theUnsafe对应的Field对象

Field field = Unsafe.class.getDeclaredField("theUnsafe");

// 设置该Field为可访问

field.setAccessible(true);

// 通过Field得到该Field对应的具体对象,传入null是因为该Field为static的

Unsafe unsafe = (Unsafe) field.get(null);

Validation v =newValidation();

System.out.println(v.sizeValidate());// false

Field f = v.getClass().getDeclaredField("MAX_SIZE");

unsafe.putInt(v, unsafe.objectFieldOffset(f),100);// memory corruption

System.out.println(v.sizeValidate());// true

  }

计算Java对象大小

有两种计算Java对象大小的方式。

通过java.lang.instrument.Instrumentation的getObjectSize(obj)直接获取对象的大小;

通过sun.misc.Unsafe对象的objectFieldOffset(field)等方法结合反射来计算对象的大小。

通过Unsafe获取Java对象大小的基本思路如下:

通过反射获得一个类的Field;

通过Unsafe的objectFieldOffset()获得每个Field的offSet;

对Field进行遍历,取得最大的offset,然后加上这个field的长度,再加上Padding对齐。

Java并发中的应用

在Java并发中会用到CAS操作,对应于Unsafe类中的compareAndSwapInt,compareAndSwapLong等。下面的例子就是使用Unsafe实现的无所数据结构。

class LongValue{

private volatile long counter =0;

private Unsafe unsafe;

private long offset;

public Long Value()throwsException{

          unsafe = getUnsafe();

            offset = unsafe.objectFieldOffset(LongValue.class.getDeclaredField("counter"));

      }

public void increment(){

    long before = counter;

    while(!unsafe.compareAndSwapLong(this, offset, before, before +1)) {

              before = counter;

          }

      }

        public long getCounter(){

        return counter;

      }

  }

看一下Java中AtomicLong的实现,下面摘出来一部分。可以看到该类在加载的时候将value的偏移位置计算出来,然后在compareAndSet等方法中使用Unsafe中的CAS操作进行替换,这样的无锁操作可以大大提高效率。

```

public class AtomicLong extends Number implements java.io.Serializable{

private stati cfinal long serialVersionUID =1927816293512124184L;

// setup to use Unsafe.compareAndSwapLong for updates

private static final Unsafe unsafe = Unsafe.getUnsafe();

private static final longvalueOffset;

    ...

static{

try{

            valueOffset = unsafe.objectFieldOffset

         (AtomicLong.class.getDeclaredField("value"));

            }catch(Exception ex) {thrownewError(ex); }

    }

        private volatile long value;

        ...

        public final boolean compareAndSet(longexpect,longupdate){

        return unsafe.compareAndSwapLong(this, valueOffset, expect, update);

    }

```

其他使用方式

通过Unsafe的defineClass可以动态加载Class;

通过Unsafe的copyMemory、freeMemory等可以实现内存的复制与释放,如果我们知道了对象的大小,利用arrayBaseOffset和copyMemory可以完成对象的浅拷贝。

Unsafe还有其他很多的用途,但是要记得这是一个非常危险的类,在使用的过程中需要万分小心,不到万不得已的情况不要使用。

相关文章

  • Unsafe类初探

    Unsafe类说明 从这个类的名字Unsafe上来说这个类就是一个不安全的类,也是不开放给用户直接使用的(当然我们...

  • sun.misc.Unsafe类 (内存操作/对象字段操作/原子

    一. 关于sun.misc.Unsafe类 sun.misc.Unsafe类的描述如下: 简单来说, Unsafe...

  • 说说Java的Unsafe类

    本文主要内容 Unsafe类介绍 Unsafe的主要功能 总结 1、Unsafe类介绍 第一次看到这个类时被它的名...

  • Unsafe类学习笔记

    Unsafe类学习笔记 Unsafe 类初识 Unsafe位于sun.misc包内,看其命名就知道和注重安全性的j...

  • Netty源码学习(6)--pipeline学习2

    Unsafe unsafe是不安全的意思,不要在应用程序里面直接使用Unsafe以及他的衍生类对象。Unsafe ...

  • Unsafe类

      这两天看netty源码看到一些UnpooledUnsafeHeapByteBuf、UnpooledUnsafe...

  • Unsafe类

    Unsafe类是Java不对外提供的不安全的类,juc包中的atomic*类都是用这个类实现的 // Uns...

  • UnSafe 类

    https://tech.meituan.com/2019/02/14/talk-about-java-magic...

  • Unsafe 类

    Unsafe类 不能直接 new, 其构造函数被私有化 public final class Unsafe : U...

  • UnSafe类中的一些重要方法

    UnSafe类中的一些重要方法 JDK中的rt.jar保重Unsafe类中提供了硬件级别的原子性操作,Unsafe...

网友评论

      本文标题:Unsafe类初探

      本文链接:https://www.haomeiwen.com/subject/ymubsftx.html