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

一. 关于sun.misc.Unsafe

sun.misc.Unsafe类的描述如下:

/**
 * A collection of methods for performing low-level, unsafe operations.
 * Although the class and all methods are public, use of this class is
 * limited because only trusted code can obtain instances of it.
 */

简单来说, Unsafe类提供了一些比较底层的不安全的操作. 因此此类只有那些可以被信任的代码才能调用. 此类只能通过其静态方法getUnsafe()获得其实例. Unsafe类的这些底层操作非常有用, 因此非常多的库或应用都使用了Unsafe的相关功能, 从此文中可窥一斑 => Removal of sun.misc.Unsafe in Java 9 - A disaster in the making. 大家不用担心, Java9中Unsafe还是可用的 => Using sun.misc.Unsafe in Java 9

Unsafe.java的部分源码如下:

public final class Unsafe {

    private Unsafe() {}

    private static final Unsafe theUnsafe = new Unsafe();

    @CallerSensitive
    public static Unsafe getUnsafe() {
        Class<?> caller = Reflection.getCallerClass();
        if (!VM.isSystemDomainLoader(caller.getClassLoader()))
            throw new SecurityException("Unsafe");
        return theUnsafe;
    }
    
    //其余部分省略 ...
}

可以看到, Unsafe类是一个单例类, 通过静态的getUnsafe()方法获取实例. getUnsafe()方法中有一个权限检查的逻辑, 即: 如果不是系统域下的类调用getUnsafe()方法将抛出SecurityException异常.

因此, 非系统类库要获取Unsafe的实例就不能直接调用getUnsafe()方法. 从上面可以看到, Unsafe类有一个类型为其本身的静态常量theUnsafe, 因此我们可以用反射来获取其实例. 代码如下:

private static void init() {
        try {
            unsafeClass = Class.forName("sun.misc.Unsafe");
            Field theUnsafeField = unsafeClass.getDeclaredField("theUnsafe");
            boolean orignialAccessible = theUnsafeField.isAccessible();
            theUnsafeField.setAccessible(true);
            unsafeInstance = theUnsafeField.get(null); //unsafeInstance就是Unsafe的实例
            theUnsafeField.setAccessible(orignialAccessible);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

Unsafe的相关功能如下:

1. 通过一个Class对象创建一个实例 (不经过构造函数直接生成对象, 这个非常厉害, 具体原理有待研究!)
  • Object allocateInstance(Class<?> cls)
    不通过构造方法就实例化出一个对象.
  • 此功能的运用
    一些常见的JSON库一般都需要一个无参构造方法来实例化一个POJO对象, 有了Unsafe类的这个功能, 就算没有构造方法, 这些JSON库也可以实例化对象了. Gson库就使用了该功能,
    详情见: com.google.gson.internal.UnsafeAllocator. 有一个专门用来创建对象 (绕开构造方法) 的库Objenesis底层也有此功能的运用.
2. 管理和操作内存

内存管理:

  • long allocateMemory(long bytes)
    开辟一块内存. 参数为要开辟内存的大小, 多少字节; 返回值是一个native pointer, 就是内存地址.
  • void freeMemory(long address)
    释放一块内存. 参数为内存地址. 即allocateMemory()方法的返回值.
  • long reallocateMemory(long address, long bytes)
    重新分配 (扩展) 一块内存 (之前分配内存当然会被GC回收掉). 第一个参数为原内存地址, 第二个参数为重新分配的内存的大小 (要超过前一块内存的大小), 返回新的内存地址. 原内存中的内容会迁移到新开辟的内存中.

内存操作 (往内存中存数据 或 从内存中取数据):

  • void putType(long address, type data)type getType(long address, long offset)
    i. type 为原始数据类型: boolean, bytecharshortintlongfloatdouble
    ii. 第一个参数address, 为内存地址(参考allocateMemory())
    iii. 第二个参数offset, 字段的偏移量, 用下列方法获取:
    • long objectFieldOffset(Field field) - 获取实例字段的偏移量
    • long staticFieldOffset(Field field) - 获取静态字段的偏移量
3. 操作对象字段的值 (无视可见性)
  • void putType(Object o, long offset, type data)type getType(Object o, long offset)
    i. type为原始数据类型和Object
    ii. 第一个参数o, 为要操作的对象 (如果是静态字段, 则第一个参数传Class<?>对象)
    iii. 第二个参数offset, 为字段的偏移量, 其获取方式同内存操作中涉及的offset的获取一样
    iiii. 字段操作还有volatile版的, 如: putObjectVolatile()getIntVolatile()
4. 原子操作
  • CAS操作
    compareAndSwapXxx(Object o, long offset, Object expected, Object newValue) - 对象字段的CAS操作
    tips: CAS操作有3个操作数,内存值M,预期值E,新值U,如果M==E,则将内存值修改为B,否则啥都不做。
  • JDK1.8: getAndAddInt()getAndSetInt()getAndAddLong()getAndSetLong()getAndAddObject()getAndSetObject() - 原子操作 => 值操作: 获取 和 更新
5. 线程操作
  • void unpark(Object thread) - 取消挂起
  • void park(boolean isAbsolute, long time) - 挂起
  • 将一个线程进行挂起是通过park方法实现的,调用park()后,线程将一直阻塞直到超时或者中断等条件出现。unpark()可以终止一个挂起的线程,使其恢复正常。整个并发框架中对线程的挂起操作被封装在 LockSupport类中,LockSupport类中有各种版本pack()方法,但最终都调用了Unsafe.park()方法。
6. 数组元素定位
  • int arrayIndexScale(Class<?> arrayClass)
  • int arrayBaseOffset(Class<?> arrayClass)
    Unsafe类中有很多以BASE_OFFSET结尾的常量,如ARRAY_INT_BASE_OFFSETARRAY_BYTE_BASE_OFFSET等,这些常量值是通过arrayBaseOffset()方法得到的。arrayBaseOffset()方法是一个本地方法,可以获取数组第一个元素的偏移地址。Unsafe类中还有很多以INDEX_SCALE结尾的常量,如 ARRAY_INT_INDEX_SCALEARRAY_BYTE_INDEX_SCALE等,这些常量值是通过arrayIndexScale()方法得到的。arrayIndexScale()方法也是一个本地方法,可以获取数组的转换因子,也就是数组中元素的增量地址。将arrayBaseOffset()arrayIndexScale()配合使用,可以定位数组中每个元素在内存中的位置。
6. 其他 (略)
  • setMemory()copyMemory()addressSize()pageSize()defineClass()defineAnonymousClass()
  • Java8: loadFence()storeFence()fullFence()
  • Java9: invokeCleaner()
  • ......

二. 为了不在代码中出现sun.misc.Unsafe相关API的直接调用 (避免直接依赖, 必要时可以更换具体实现) , 我使用反射对sun.misc.Unsafe进行了封装 (不常用的方法省略), 封装类UnsafeWrapper


下面是封装类UnsafeWrapper的一个使用Demo (具体说明, 请看其中的注释, UnsafeWrapper类的源码在Demo之后) :
package com;

import com.stone.UnsafeWrapper;

import java.io.UnsupportedEncodingException;

//http://hg.openjdk.java.net/jdk9/client/jdk/file/adc00ab4ac58/src/jdk.unsupported/share/classes/sun/misc/Unsafe.java
//http://hg.openjdk.java.net/jdk9/client/jdk/file/65464a307408/src/java.base/share/classes/jdk/internal/misc/Unsafe.java
public class Demo {
    private int fieldA = 2;
    private static String fieldB = "Unknown";
    
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, UnsupportedEncodingException {
        demo0();
        demo1();
        demo2();
    }

    //创建对象, 而不经过构造方法
    private static void demo0() {
        Demo t2 = UnsafeWrapper.allocateInstance(Demo.class);
        System.out.println("demo0: Test2 = " + t2);
        System.out.println("demo0: fieldA = " + t2.fieldA);
        System.out.println("demo0: fieldB = " + Demo.fieldB);
    }

    //操作一个对象的属性 (private修饰的属性也可以操作)
    static void demo1() throws NoSuchFieldException {
        /**
        try {
            //不能直接调用Unsafe.getUnsafe()方法: Exception in thread "main" java.lang.SecurityException: Unsafe
            //Unsafe unsafe = Unsafe.getUnsafe();

            //使用反射获取Unsafe的实例, 绕过安全检查
            Unsafe unsafe = getUnsafe();
            Test2 t = new Test2();
            Field aField = Test2.class.getDeclaredField("fieldA");
            int aValue = unsafe.getInt(t, unsafe.objectFieldOffset(aField));
            System.out.println("fieldA = " + aValue);


            unsafe.putInt(t, unsafe.objectFieldOffset(aField), 102);
            aValue = unsafe.getInt(t, unsafe.objectFieldOffset(aField));
            System.out.println("fieldA = " + aValue);
        } catch(Exception e) {
            e.printStackTrace();
        }*/


        ///////////操作实例属性/////////////////
        Demo t = new Demo();
        System.out.println("primitive type: ");
        int fieldA = UnsafeWrapper.getInt(t, UnsafeWrapper.objectFieldOffset(Demo.class.getDeclaredField("fieldA")));
        System.out.println("fieldA value (before change) = " + fieldA);

        UnsafeWrapper.putInt(t, UnsafeWrapper.objectFieldOffset(Demo.class.getDeclaredField("fieldA")), 5023);
        fieldA = UnsafeWrapper.getInt(t, UnsafeWrapper.objectFieldOffset(Demo.class.getDeclaredField("fieldA")));
        System.out.println("fieldA value (after change) = " + fieldA);


        ///////////操作静态属性/////////////////
        System.out.println("reference type: ");
        String fieldB = (String) UnsafeWrapper.getObject(Demo.class, UnsafeWrapper.staticFieldOffset(Demo.class.getDeclaredField("fieldB")));
        System.out.println("fieldB value: " + fieldB);


        UnsafeWrapper.putObject(Demo.class, UnsafeWrapper.staticFieldOffset(Demo.class.getDeclaredField("fieldB")), "呵呵呵呵");
        fieldB = (String) UnsafeWrapper.getObject(Demo.class, UnsafeWrapper.staticFieldOffset(Demo.class.getDeclaredField("fieldB")));
        System.out.println("fieldB value: " + fieldB);
    }

    //管理和操作内存
    static void demo2() throws UnsupportedEncodingException {
        //开辟一块内存
        long initMemorySize = 20;
        long address = UnsafeWrapper.allocateMemory(initMemorySize);
        System.out.println("original memory address: " + address);

        String greeting = "Hello world !";
        byte[] bytes = greeting.getBytes("utf-8");

        //将byte[]放入手动开辟的内存块中
        UnsafeWrapper.putByteArray(address, bytes);

        //从内指定的内存块中取出数据
        byte[] readedBytes = UnsafeWrapper.readByteArray(address, bytes.length);
        System.out.println("content in original memory: " + new String(readedBytes, "utf-8"));


        //扩展内存大小
        long extMemorySize = 1024; //扩展的内存大小必须比之前分配的内存大
        address = UnsafeWrapper.reallocateMemory(address, extMemorySize);
        System.out.println("reallocate memory address: " + address);
        readedBytes = UnsafeWrapper.readByteArray(address, bytes.length);
        System.out.println("content in reallocate memory: " + new String(readedBytes, "utf-8"));

        //释放内存
        UnsafeWrapper.freeMemory(address);
    }
}



UnsafeWrapper类源码如下:

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public final class UnsafeWrapper {

    private static Class unsafeClass;
    private static Object unsafeInstance;

    private UnsafeWrapper() {
        //no instance
    }

    /** ==============================================*/
    /** ====================内存块操作==================*/
    /** ==============================================*/

    /**
     * Allocates a new block of native memory, of the given size in bytes.  The
     * contents of the memory are uninitialized; they will generally be
     * garbage.  The resulting native pointer will never be zero, and will be
     * aligned for all value types.  Dispose of this memory by calling {@link
     * #freeMemory}, or resize it with {@link #reallocateMemory}.
     *
     * <em>Note:</em> It is the resposibility of the caller to make
     * sure arguments are checked before the methods are called. While
     * some rudimentary checks are performed on the input, the checks
     * are best effort and when performance is an overriding priority,
     * as when methods of this class are optimized by the runtime
     * compiler, some or all checks (if any) may be elided. Hence, the
     * caller must not rely on the checks and corresponding
     * exceptions!
     *
     * @throws RuntimeException if the size is negative or too large
     *         for the native size_t type
     *
     * @throws OutOfMemoryError if the allocation is refused by the system
     *
     * @see #getByte(long)
     * @see #putByte(long, byte)
     */
    public static long allocateMemory(long bytes) {
        long address = 0;
        try {
            Method allocateMemoryMethod = unsafeClass().getDeclaredMethod("allocateMemory", long.class);
            address = (long) allocateMemoryMethod.invoke(unsafeInstance(), bytes);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        return address;
    }

    /**
     * Resizes a new block of native memory, to the given size in bytes.  The
     * contents of the new block past the size of the old block are
     * uninitialized; they will generally be garbage.  The resulting native
     * pointer will be zero if and only if the requested size is zero.  The
     * resulting native pointer will be aligned for all value types.  Dispose
     * of this memory by calling {@link #freeMemory}, or resize it with {@link
     * #reallocateMemory}.  The address passed to this method may be null, in
     * which case an allocation will be performed.
     *
     * <em>Note:</em> It is the resposibility of the caller to make
     * sure arguments are checked before the methods are called. While
     * some rudimentary checks are performed on the input, the checks
     * are best effort and when performance is an overriding priority,
     * as when methods of this class are optimized by the runtime
     * compiler, some or all checks (if any) may be elided. Hence, the
     * caller must not rely on the checks and corresponding
     * exceptions!
     *
     * @throws RuntimeException if the size is negative or too large
     *         for the native size_t type
     *
     * @throws OutOfMemoryError if the allocation is refused by the system
     *
     * @see #allocateMemory
     */
    public static long reallocateMemory(long address, long bytes) {
        long result = 0;
        try {
            Method allocateMemoryMethod = unsafeClass().getDeclaredMethod("reallocateMemory", long.class, long.class);
            result = (long) allocateMemoryMethod.invoke(unsafeInstance(), address, bytes);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        return result;
    }

    /**
     * Disposes of a block of native memory, as obtained from {@link
     * #allocateMemory} or {@link #reallocateMemory}.  The address passed to
     * this method may be null, in which case no action is taken.
     *
     * <em>Note:</em> It is the resposibility of the caller to make
     * sure arguments are checked before the methods are called. While
     * some rudimentary checks are performed on the input, the checks
     * are best effort and when performance is an overriding priority,
     * as when methods of this class are optimized by the runtime
     * compiler, some or all checks (if any) may be elided. Hence, the
     * caller must not rely on the checks and corresponding
     * exceptions!
     *
     * @throws RuntimeException if any of the arguments is invalid
     *
     * @see #allocateMemory
     */
    public static void freeMemory(long address) {
        try {
            Method freeMemoryMethod = unsafeClass().getDeclaredMethod("freeMemory", long.class);
            freeMemoryMethod.invoke(unsafeInstance(), address);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * Fetches a value from a given memory address.  If the address is zero, or
     * does not point into a block obtained from {@link #allocateMemory}, the
     * results are undefined.
     *
     * @see #allocateMemory
     */
    public static byte getByte(long address) {
        byte result = 0;
        try {
            Method allocateMemoryMethod = unsafeClass().getDeclaredMethod("getByte", long.class);
            result = (byte) allocateMemoryMethod.invoke(unsafeInstance(), address);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        return result;
    }

    /**
     * Stores a value into a given memory address.  If the address is zero, or
     * does not point into a block obtained from {@link #allocateMemory}, the
     * results are undefined.
     *
     * @see #getByte(long)
     */
    public static void putByte(long address, byte x) {
        try {
            Method allocateMemoryMethod = unsafeClass().getDeclaredMethod("putByte", long.class, byte.class);
            allocateMemoryMethod.invoke(unsafeInstance(), address, x);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }


    /**
     * 从内存中读取一个数组
     *
     * @param startAddress  内存起始地址
     * @param len           要读取的数组的长度
     * @return              返回从内存中读取到的数组
     */
    public static byte[] readByteArray(long startAddress, int len) {
        byte[] bytes = new byte[len];

        long currentAddress = startAddress;
        long lastAddress = startAddress + len;
        while(currentAddress < lastAddress) {
            int currentIndex = (int) (currentAddress - startAddress);
            bytes[currentIndex] = getByte(currentAddress);
            currentAddress++;
        }

        return bytes;
    }

    /**
     * 把一个byte[]放到指定的内存块中
     *
     * @param startAddress  内存块的起始位置
     * @param bytes         要被存放的内存
     */
    public static void putByteArray(long startAddress, byte[] bytes) {
        long currentAddress = startAddress;
        long lastAddress = startAddress + bytes.length;
        while(currentAddress < lastAddress) {
            int currentIndex = (int) (currentAddress - startAddress);
            putByte(currentAddress, bytes[currentIndex]);
            currentAddress++;
        }
    }

    /** @see #getByte(long) */
    public static short getShort(long address) {
        short result = 0;
        try {
            Method method = unsafeClass().getDeclaredMethod("getShort", long.class);
            result = (short) method.invoke(unsafeInstance(), address);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

    /** @see #putByte(long, byte) */
    public static void putShort(long address, short x) {
        try {
            Method method = unsafeClass().getDeclaredMethod("putShort", long.class, short.class);
            method.invoke(unsafeInstance(), address, x);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /** @see #getByte(long) */
    public static char getChar(long address) {
        char result = 0;
        try {
            Method method = unsafeClass().getDeclaredMethod("getChar", long.class);
            result = (char) method.invoke(unsafeInstance(), address);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

    /** @see #putByte(long, byte) */
    public static void putChar(long address, char x) {
        try {
            Method method = unsafeClass().getDeclaredMethod("putChar", long.class, char.class);
            method.invoke(unsafeInstance(), address, x);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /** @see #getByte(long) */
    public static int getInt(long address) {
        int result = 0;
        try {
            Method method = unsafeClass().getDeclaredMethod("getInt", long.class);
            result = (int) method.invoke(unsafeInstance(), address);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

    /** @see #putByte(long, byte) */
    public static void putInt(long address, int x) {
        try {
            Method method = unsafeClass().getDeclaredMethod("putInt", long.class, int.class);
            method.invoke(unsafeInstance(), address, x);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /** @see #getByte(long) */
    public static long getLong(long address) {
        long result = 0;
        try {
            Method method = unsafeClass().getDeclaredMethod("getLong", long.class);
            result = (long) method.invoke(unsafeInstance(), address);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

    /** @see #putByte(long, byte) */
    public static void putLong(long address, long x) {
        try {
            Method method = unsafeClass().getDeclaredMethod("putLong", long.class, long.class);
            method.invoke(unsafeInstance(), address, x);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /** @see #getByte(long) */
    public static float getFloat(long address) {
        float result = 0;
        try {
            Method method = unsafeClass().getDeclaredMethod("getFloat", long.class);
            result = (float) method.invoke(unsafeInstance(), address);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

    /** @see #putByte(long, byte) */
    public static void putFloat(long address, float x) {
        try {
            Method method = unsafeClass().getDeclaredMethod("putFloat", long.class, float.class);
            method.invoke(unsafeInstance(), address, x);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /** @see #getByte(long) */
    public static double getDouble(long address) {
        double result = 0;
        try {
            Method method = unsafeClass().getDeclaredMethod("getDouble", long.class);
            result = (double) method.invoke(unsafeInstance(), address);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

    /** @see #putByte(long, byte) */
    public static void putDouble(long address, double x) {
        try {
            Method method = unsafeClass().getDeclaredMethod("putDouble", long.class, double.class);
            method.invoke(unsafeInstance(), address, x);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }






    /** ==============================================*/
    /** ====================对象操作====================*/
    /** ==============================================*/

    /**
     * Allocates an instance but does not run any constructor.
     * Initializes the class if it has not yet been.
     */
    public static <T> T allocateInstance(Class<T> klass) {

        T result = null;

        try {
            Method allocateInstanceMethod = unsafeClass().getDeclaredMethod("allocateInstance", Class.class);
            result = (T) allocateInstanceMethod.invoke(unsafeInstance(), klass);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return result;
    }

    /** @see #getInt(Object, long) */
    public static byte getByte(Object o, long offset) {
        byte result = 0;
        try {
            Method method = unsafeClass().getDeclaredMethod("getByte", Object.class, long.class);
            result = (byte) method.invoke(unsafeInstance(), o, offset);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

    /** @see #putInt(Object, long, int) */
    public static void putByte(Object o, long offset, byte x) {
        try {
            Method method = unsafeClass().getDeclaredMethod("putByte", Object.class, long.class, byte.class);
            method.invoke(unsafeInstance(), o, offset, x);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * Fetches a value from a given Java variable.
     * More specifically, fetches a field or array element within the given
     * object {@code o} at the given offset, or (if {@code o} is null)
     * from the memory address whose numerical value is the given offset.
     * <p>
     * The results are undefined unless one of the following cases is true:
     * <ul>
     * <li>The offset was obtained from {@link #objectFieldOffset} on
     * the {@link java.lang.reflect.Field} of some Java field and the object
     * referred to by {@code o} is of a class compatible with that
     * field's class.
     *
     * <li>The offset and object reference {@code o} (either null or
     * non-null) were both obtained via {@link #staticFieldOffset}
     * and {@link #staticFieldBase} (respectively) from the
     * reflective {@link Field} representation of some Java field.
     *
     * <li>The object referred to by {@code o} is an array, and the offset
     * is an integer of the form {@code B+N*S}, where {@code N} is
     * a valid index into the array, and {@code B} and {@code S} are
     * the values obtained by {@link #arrayBaseOffset} and {@link
     * #arrayIndexScale} (respectively) from the array's class.  The value
     * referred to is the {@code N}<em>th</em> element of the array.
     *
     * </ul>
     * <p>
     * If one of the above cases is true, the call references a specific Java
     * variable (field or array element).  However, the results are undefined
     * if that variable is not in fact of the type returned by this method.
     * <p>
     * This method refers to a variable by means of two parameters, and so
     * it provides (in effect) a <em>double-register</em> addressing mode
     * for Java variables.  When the object reference is null, this method
     * uses its offset as an absolute address.  This is similar in operation
     * to methods such as {@link #getInt(long)}, which provide (in effect) a
     * <em>single-register</em> addressing mode for non-Java variables.
     * However, because Java variables may have a different layout in memory
     * from non-Java variables, programmers should not assume that these
     * two addressing modes are ever equivalent.  Also, programmers should
     * remember that offsets from the double-register addressing mode cannot
     * be portably confused with longs used in the single-register addressing
     * mode.
     *
     * @param o Java heap object in which the variable resides, if any, else
     *        null
     * @param offset indication of where the variable resides in a Java heap
     *        object, if any, else a memory address locating the variable
     *        statically
     * @return the value fetched from the indicated Java variable
     * @throws RuntimeException No defined exceptions are thrown, not even
     *         {@link NullPointerException}
     */
    public static int getInt(Object o, long offset) {
        int result = 0;
        try {
            Method method = unsafeClass().getDeclaredMethod("getInt", Object.class, long.class);
            result = (int) method.invoke(unsafeInstance(), o, offset);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * Stores a value into a given Java variable.
     * <p>
     * The first two parameters are interpreted exactly as with
     * {@link #getInt(Object, long)} to refer to a specific
     * Java variable (field or array element).  The given value
     * is stored into that variable.
     * <p>
     * The variable must be of the same type as the method
     * parameter {@code x}.
     *
     * @param o Java heap object in which the variable resides, if any, else
     *        null
     * @param offset indication of where the variable resides in a Java heap
     *        object, if any, else a memory address locating the variable
     *        statically
     * @param x the value to store into the indicated Java variable
     * @throws RuntimeException No defined exceptions are thrown, not even
     *         {@link NullPointerException}
     */
    public static void putInt(Object o, long offset, int x) {
        try {
            Method method = unsafeClass().getDeclaredMethod("putInt", Object.class, long.class, int.class);
            method.invoke(unsafeInstance(), o, offset, x);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * Fetches a reference value from a given Java variable.
     * @see #getInt(Object, long)
     */
    public static Object getObject(Object o, long offset) {
        Object result = null;
        try {
            Method method = unsafeClass().getDeclaredMethod("getObject", Object.class, long.class);
            result = method.invoke(unsafeInstance(), o, offset);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }


    /**
     * Stores a reference value into a given Java variable.
     * <p>
     * Unless the reference {@code x} being stored is either null
     * or matches the field type, the results are undefined.
     * If the reference {@code o} is non-null, card marks or
     * other store barriers for that object (if the VM requires them)
     * are updated.
     * @see #putInt(Object, long, int)
     */
    public static void putObject(Object o, long offset, Object x) {
        try {
            Method method = unsafeClass().getDeclaredMethod("putObject", Object.class, long.class, Object.class);
            method.invoke(unsafeInstance(), o, offset, x);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }


    /** @see #getInt(Object, long) */
    public static boolean getBoolean(Object o, long offset) {
        boolean result = false;
        try {
            Method method = unsafeClass().getDeclaredMethod("getBoolean", Object.class, long.class);
            result = (boolean) method.invoke(unsafeInstance(), o, offset);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

    /** @see #putInt(Object, long, int) */
    public static void putBoolean(Object o, long offset, boolean x) {
        try {
            Method method = unsafeClass().getDeclaredMethod("putBoolean", Object.class, long.class, boolean.class);
            method.invoke(unsafeInstance(), o, offset, x);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /** @see #getInt(Object, long) */
    public static short getShort(Object o, long offset) {
        short result = 0;
        try {
            Method method = unsafeClass().getDeclaredMethod("getShort", Object.class, long.class);
            result = (short) method.invoke(unsafeInstance(), o, offset);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

    /** @see #putInt(Object, long, int) */
    public static void putShort(Object o, long offset, short x) {
        try {
            Method method = unsafeClass().getDeclaredMethod("putShort", Object.class, long.class, short.class);
            method.invoke(unsafeInstance(), o, offset, x);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /** @see #getInt(Object, long) */
    public static char getChar(Object o, long offset) {
        char result = 0;
        try {
            Method method = unsafeClass().getDeclaredMethod("getChar", Object.class, long.class);
            result = (char) method.invoke(unsafeInstance(), o, offset);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

    /** @see #putInt(Object, long, int) */
    public static void putChar(Object o, long offset, char x) {
        try {
            Method method = unsafeClass().getDeclaredMethod("putChar", Object.class, long.class, char.class);
            method.invoke(unsafeInstance(), o, offset, x);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /** @see #getInt(Object, long) */
    public static long getLong(Object o, long offset) {
        long result = 0;
        try {
            Method method = unsafeClass().getDeclaredMethod("getLong", Object.class, long.class);
            result = (long) method.invoke(unsafeInstance(), o, offset);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

    /** @see #putInt(Object, long, int) */
    public static void putLong(Object o, long offset, long x) {
        try {
            Method method = unsafeClass().getDeclaredMethod("putLong", Object.class, long.class, long.class);
            method.invoke(unsafeInstance(), o, offset, x);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /** @see #getInt(Object, long) */
    public static float getFloat(Object o, long offset) {
        float result = 0f;
        try {
            Method method = unsafeClass().getDeclaredMethod("getFloat", Object.class, long.class);
            result = (float) method.invoke(unsafeInstance(), o, offset);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

    /** @see #putInt(Object, long, int) */
    public static void putFloat(Object o, long offset, float x) {
        try {
            Method method = unsafeClass().getDeclaredMethod("putFloat", Object.class, long.class, float.class);
            method.invoke(unsafeInstance(), o, offset, x);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /** @see #getInt(Object, long) */
    public static double getDouble(Object o, long offset) {
        double result = 0;
        try {
            Method method = unsafeClass().getDeclaredMethod("getDouble", Object.class, long.class);
            result = (double) method.invoke(unsafeInstance(), o, offset);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

    /** @see #putInt(Object, long, int) */
    public static void putDouble(Object o, long offset, double x) {
        try {
            Method method = unsafeClass().getDeclaredMethod("putDouble", Object.class, long.class, double.class);
            method.invoke(unsafeInstance(), o, offset, x);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }




    /** ==============================================*/
    /** ================偏移量操作======================*/
    /** ==============================================*/
    /**
     * Reports the location of a given field in the storage allocation of its
     * class.  Do not expect to perform any sort of arithmetic on this offset;
     * it is just a cookie which is passed to the unsafe heap memory accessors.
     *
     * <p>Any given field will always have the same offset and base, and no
     * two distinct fields of the same class will ever have the same offset
     * and base.
     *
     * <p>As of 1.4.1, offsets for fields are represented as long values,
     * although the Sun JVM does not use the most significant 32 bits.
     * However, JVM implementations which store static fields at absolute
     * addresses can use long offsets and null base pointers to express
     * the field locations in a form usable by {@link #getInt(Object,long)}.
     * Therefore, code which will be ported to such JVMs on 64-bit platforms
     * must preserve all bits of static field offsets.
     * @see #getInt(Object, long)
     */
    public static long objectFieldOffset(Field field) {
        long result = 0;
        try {
            Method method = unsafeClass().getDeclaredMethod("objectFieldOffset", Field.class);
            result = (long) method.invoke(unsafeInstance(), field);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * Reports the location of a given static field, in conjunction with {@link
     * #staticFieldBase}.
     * <p>Do not expect to perform any sort of arithmetic on this offset;
     * it is just a cookie which is passed to the unsafe heap memory accessors.
     *
     * <p>Any given field will always have the same offset, and no two distinct
     * fields of the same class will ever have the same offset.
     *
     * <p>As of 1.4.1, offsets for fields are represented as long values,
     * although the Sun JVM does not use the most significant 32 bits.
     * It is hard to imagine a JVM technology which needs more than
     * a few bits to encode an offset within a non-array object,
     * However, for consistency with other methods in this class,
     * this method reports its result as a long value.
     * @see #getInt(Object, long)
     */
    public static long staticFieldOffset(Field field) {
        long result = 0;
        try {
            Method method = unsafeClass().getDeclaredMethod("staticFieldOffset", Field.class);
            result = (long) method.invoke(unsafeInstance(), field);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * Reports the location of a given static field, in conjunction with {@link
     * #staticFieldOffset}.
     * <p>Fetch the base "Object", if any, with which static fields of the
     * given class can be accessed via methods like {@link #getInt(Object,
     * long)}.  This value may be null.  This value may refer to an object
     * which is a "cookie", not guaranteed to be a real Object, and it should
     * not be used in any way except as argument to the get and put routines in
     * this class.
     */
    public static Object staticFieldBase(Field field) {
        Object result = false;
        try {
            Method method = unsafeClass().getDeclaredMethod("staticFieldBase", Field.class);
            result = method.invoke(unsafeInstance(), field);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }


    /**
     * Reports the offset of the first element in the storage allocation of a
     * given array class.  If {@link #arrayIndexScale} returns a non-zero value
     * for the same class, you may use that scale factor, together with this
     * base offset, to form new offsets to access elements of arrays of the
     * given class.
     *
     * @see #getInt(Object, long)
     * @see #putInt(Object, long, int)
     */
    public static int arrayBaseOffset(Class<?> arrayClass) {
        int result = 0;
        try {
            Method method = unsafeClass().getDeclaredMethod("arrayBaseOffset", Class.class);
            result = (int) method.invoke(unsafeInstance(), arrayClass);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * Reports the scale factor for addressing elements in the storage
     * allocation of a given array class.  However, arrays of "narrow" types
     * will generally not work properly with accessors like {@link
     * #getByte(Object, long)}, so the scale factor for such classes is reported
     * as zero.
     *
     * @see #arrayBaseOffset
     * @see #getInt(Object, long)
     * @see #putInt(Object, long, int)
     */
    public static int arrayIndexScale(Class<?> arrayClass) {
        int result = 0;
        try {
            Method method = unsafeClass().getDeclaredMethod("arrayIndexScale", Class.class);
            result = (int) method.invoke(unsafeInstance(), arrayClass);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }


    /** ==============================================*/
    /** ==================CAS操作======================*/
    /** ==============================================*/
    /**
     * Atomically updates Java variable to {@code x} if it is currently
     * holding {@code expected}.
     *
     * <p>This operation has memory semantics of a {@code volatile} read
     * and write.  Corresponds to C11 atomic_compare_exchange_strong.
     *
     * @return {@code true} if successful
     */
    public static boolean compareAndSwapObject(Object o, long offset, Object expected, Object x) {
        boolean result = false;
        try {
            Method method = unsafeClass().getDeclaredMethod("compareAndSwapObject", Object.class, long.class, Object.class, Object.class);
            result = (boolean) method.invoke(unsafeInstance(), o, offset, expected, x);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * Atomically updates Java variable to {@code x} if it is currently
     * holding {@code expected}.
     *
     * <p>This operation has memory semantics of a {@code volatile} read
     * and write.  Corresponds to C11 atomic_compare_exchange_strong.
     *
     * @return {@code true} if successful
     */
    public static boolean compareAndSwapInt(Object o, long offset, int expected, int x) {
        boolean result = false;
        try {
            Method method = unsafeClass().getDeclaredMethod("compareAndSwapInt", Object.class, long.class, int.class, int.class);
            result = (boolean) method.invoke(unsafeInstance(), o, offset, expected, x);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * Atomically updates Java variable to {@code x} if it is currently
     * holding {@code expected}.
     *
     * <p>This operation has memory semantics of a {@code volatile} read
     * and write.  Corresponds to C11 atomic_compare_exchange_strong.
     *
     * @return {@code true} if successful
     */
    public static boolean compareAndSwapLong(Object o, long offset, long expected, long x) {
        boolean result = false;
        try {
            Method method = unsafeClass().getDeclaredMethod("compareAndSwapLong", Object.class, long.class, long.class, long.class);
            result = (boolean) method.invoke(unsafeInstance(), o, offset, expected, x);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }


    /**
     * Unblocks the given thread blocked on {@code park}, or, if it is
     * not blocked, causes the subsequent call to {@code park} not to
     * block.  Note: this operation is "unsafe" solely because the
     * caller must somehow ensure that the thread has not been
     * destroyed. Nothing special is usually required to ensure this
     * when called from Java (in which there will ordinarily be a live
     * reference to the thread) but this is not nearly-automatically
     * so when calling from native code.
     *
     * @param thread the thread to unpark.
     */
    public void unpark(Object thread) {
        try {
            Method method = unsafeClass().getDeclaredMethod("park", Object.class);
            method.invoke(unsafeInstance(), thread);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * Blocks current thread, returning when a balancing
     * {@code unpark} occurs, or a balancing {@code unpark} has
     * already occurred, or the thread is interrupted, or, if not
     * absolute and time is not zero, the given time nanoseconds have
     * elapsed, or if absolute, the given deadline in milliseconds
     * since Epoch has passed, or spuriously (i.e., returning for no
     * "reason"). Note: This operation is in the Unsafe class only
     * because {@code unpark} is, so it would be strange to place it
     * elsewhere.
     */
    public void park(boolean isAbsolute, long time) {
        try {
            Method method = unsafeClass().getDeclaredMethod("park", boolean.class, long.class);
            method.invoke(unsafeInstance(), isAbsolute, time);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    private static void init() {
        try {
            unsafeClass = Class.forName("sun.misc.Unsafe");
            Field theUnsafeField = unsafeClass.getDeclaredField("theUnsafe");
            boolean orignialAccessible = theUnsafeField.isAccessible();
            theUnsafeField.setAccessible(true);
            unsafeInstance = theUnsafeField.get(null);
            theUnsafeField.setAccessible(orignialAccessible);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    private static Object unsafeInstance() {
        if(unsafeInstance == null) init();
        return unsafeInstance;
    }

    private static Class unsafeClass() {
        if(unsafeClass == null) init();
        return unsafeClass;
    }

}

References:

http://openjdk.java.net/
http://hg.openjdk.java.net/jdk9/client/jdk/file/adc00ab4ac58/src/jdk.unsupported/share/classes/sun/misc/Unsafe.java
http://mishadoff.com/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/
http://gregluck.com/blog/archives/2017/03/using-sun-misc-unsafe-in-java-9/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,306评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,657评论 2 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,928评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,688评论 0 220
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,105评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,024评论 1 225
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,159评论 2 318
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,937评论 0 212
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,689评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,851评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,325评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,651评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,364评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,192评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,985评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,154评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,955评论 2 279

推荐阅读更多精彩内容

  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,541评论 6 13
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,021评论 11 349
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,111评论 18 139
  • Java8张图 11、字符串不变性 12、equals()方法、hashCode()方法的区别 13、...
    Miley_MOJIE阅读 3,639评论 0 11
  • 这里所说的内容类产品包括知乎等内容型社区、Flipboard等订阅+推送类产品、今日头条等资讯类产品,甚至可以延伸...
    Mr_茶客阅读 6,874评论 17 70