JDK源码(一)Unsafe

594 阅读3分钟

1.JAVA提供的操作内存类

    sun.misc.Unsafe 操作类

此类在提升Java运行效率,很多Java基础类库都使用了此类以提升性能。
此类使Java拥有了像C语言的指针一样操作内存空间的能力,但是也使程序易错。

2.代码片段

    @CallerSensitive
    public static Unsafe getUnsafe() {
        Class var0 = Reflection.getCallerClass();
        if(var0.getClassLoader() != null) {
            throw new SecurityException("Unsafe");
        } else {
            return theUnsafe;
        }
    }

getUnsafe()方法只能由ClassLoader == null的类调用,意味着调用这个方法的类是jdk中C代码加载的。

3.可以使用的方式

    public static Unsafe getUnsafe() {
        try {
            final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
            unsafeField.setAccessible(true);
            // the unsafe instance
            return (Unsafe) unsafeField.get(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

使用反射可以获取到Unsafe操作类实例。

4.Unsafe基本操作

  • 操作成员变量
    public class Dog {
        private String name;
        private int age;
        private long size;
    }
    public static void main(String[] args) throws Exception{
        Unsafe unsafe = getUnsafe();
        Dog dog = new Dog();
        // 获取name属性偏移量
        long nameOffset = unsafe.objectFieldOffset(Dog.class.getDeclaredField("name"));
        unsafe.putObject(dog, nameOffset, "Rick");
        // 获取age属性偏移量
        long ageOffset = unsafe.objectFieldOffset(Dog.class.getDeclaredField("age"));
        unsafe.putInt(dog, ageOffset, 11);
        // 获取size属性偏移量
        long sizeOffset = unsafe.objectFieldOffset(Dog.class.getDeclaredField("size"));
        unsafe.putLong(dog, sizeOffset, 1000L);
        System.out.println(dog);
    }
  • 操作数组
    @Test
    public void testArray(){
        Unsafe unsafe = UnsafeMain.getUnsafe();
        // pigs[]数组基本偏移量
        int base = unsafe.arrayBaseOffset(Pig[].class);
        // pigs[]数组的单位偏移量
        int indexScale = unsafe.arrayIndexScale(Pig[].class);
        // unsafe操作数组
        Pig[] pigs = new Pig[8];
        Pig pig = new Pig("Rick", 10, 800L);
        unsafe.compareAndSwapObject(pigs, base + 5 * indexScale, null, pig);
        System.out.println(pigs[5]);
    }
  • 内存操作
    @Test
    public void testMemory(){
        Unsafe unsafe = UnsafeMain.getUnsafe();
        // 申请内存
        long index = unsafe.allocateMemory(1L);
        // 设置内存
        unsafe.putByte(index, (byte)-127);
        // 根据地址获取数据
        System.out.println(unsafe.getByte(index));
        // 释放内存
        unsafe.freeMemory(index);
    }
  • 多线程同步(包括锁机制,CAS操作等)

这部分包括了monitorEnter、tryMonitorEnter、monitorExit、compareAndSwapInt、compareAndSwap等方法。

其中monitorEnter、tryMonitorEnter、monitorExit已经被标记为deprecated,不建议使用。

Unsafe类的CAS操作可能是用的最多的,它为Java的锁机制提供了一种新的解决办法,比如AtomicInteger等类都是通过该方法来实现的。compareAndSwap方法是原子的,可以避免繁重的锁机制,提高代码效率。这是一种乐观锁,通常认为在大部分情况下不出现竞态条件,如果操作失败,会不断重试直到成功。

  • 挂起与恢复

这部分包括了park、unpark等方法。

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

  • 内存屏障

这部分包括了loadFence、storeFence、fullFence等方法。这是在Java 8新引入的,用于定义内存屏障,避免代码重排序。

loadFence() 表示该方法之前的所有load操作在内存屏障之前完成。同理storeFence()表示该方法之前的所有store操作在内存屏障之前完成。fullFence()表示该方法之前的所有load、store操作在内存屏障之前完成。

5.总结

一些JDK源码中使用了此Unsafe,如ConcurrentHashMap等。为之后的分析做准备。