Java程序員必須要了解的類Unsafe

前言

Java是一個(gè)安全的編程語(yǔ)言,它能最大程度的防止程序員犯一些低級(jí)的錯(cuò)誤(大部分是和內(nèi)存管理有關(guān)的)。但凡是不是絕對(duì)的,使用Unsafe程序員就可以操作內(nèi)存,因此可能帶來一個(gè)安全隱患。

這篇文章是就快速學(xué)習(xí)下sun.misc.Unsafe的公共API和一些有趣的使用例子。

Unsafe 實(shí)例化

在使用Unsafe之前我們需要先實(shí)例化它。但我們不能通過像Unsafe unsafe = new Unsafe()這種簡(jiǎn)單的方式來實(shí)現(xiàn)Unsafe的實(shí)例化,這是由于Unsafe的構(gòu)造方法是私有的。Unsafe有一個(gè)靜態(tài)的getUnsafe()方法,但是如果天真的以為調(diào)用該方法就可以的話,那你將遇到一個(gè)SecurityException異常,這是由于該方法只能在被信任的代碼中調(diào)用。

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

那Java是如何判斷我們的代碼是否是受信的呢?它就是通過判斷加載我們代碼的類加載器是否是根類加載器。

我們可是通過這種方法將我們自己的代碼變?yōu)槭苄诺模褂胘vm參數(shù)bootclasspath。如下所示:

java -Xbootclasspath:/usr/jdk1.7.0/jre/lib/rt.jar:. com.mishadoff.magic.UnsafeClient

但這種方式太難了

Unsafe類內(nèi)部有一個(gè)名為theUnsafe的私有實(shí)例變量,我們可以通過反射來獲取該實(shí)例變量。

Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);

注意: 忽略你的IDE提示. 例如, eclipse可能會(huì)報(bào)這樣的錯(cuò)誤”Access restriction…” 單如果你運(yùn)行你的代碼,會(huì)發(fā)現(xiàn)一切正常。如果還是還是提示錯(cuò)誤,你可以通過如下的方式關(guān)閉該錯(cuò)誤提示:

Preferences -> Java -> Compiler -> Errors/Warnings ->
Deprecated and restricted API -> Forbidden reference -> Warning           

Unsafe API

sun.misc.Unsafe 由150個(gè)方法組成。事實(shí)上這些方法只有幾組是非常重要的用來操作不同的對(duì)象。下面我們就來看下這些方法中的一部分。

  1. Info 僅僅是返回一個(gè)低級(jí)別的內(nèi)存相關(guān)的信息
    • addressSize
    • pageSize
  2. Objects. 提供操作對(duì)象和對(duì)象字段的方法
    • allocateInstance
    • objectFieldOffset
  3. Classes. 提供針對(duì)類和類的靜態(tài)字段操作的方法
    • staticFieldOffset
    • defineClass
    • defineAnonymousClass
    • ensureClassInitialized
  4. Arrays. 數(shù)組操作
    • arrayBaseOffset
    • arrayIndexScale
  5. Synchronization. 低級(jí)別的同步原語(yǔ)
    • monitorEnter
    • tryMonitorEnter
    • monitorExit
    • compareAndSwapInt
    • putOrderedInt
  6. Memory. 直接訪問內(nèi)存的方法
    • allocateMemory
    • copyMemory
    • freeMemory
    • getAddress
    • getInt
    • putInt

有趣的使用case

跳過構(gòu)造初始化

allocateInstance方法可能是有用的,當(dāng)你需要在構(gòu)造函數(shù)中跳過對(duì)象初始化階段或繞過安全檢查又或者你想要實(shí)例化哪些沒有提供公共構(gòu)造函數(shù)的類時(shí)就可以使用該方法。考慮下面的類:

class A {
    private long a; // not initialized value

    public A() {
        this.a = 1; // initialization
    }

    public long a() { return this.a; }
}

通過構(gòu)造函數(shù),反射,Unsafe分別來實(shí)例化該類結(jié)果是不同的:

A o1 = new A(); // constructor
o1.a(); // prints 1

A o2 = A.class.newInstance(); // reflection
o2.a(); // prints 1

A o3 = (A) unsafe.allocateInstance(A.class); // unsafe
o3.a(); // prints 0

思考一下這些確保對(duì)Singletons模式的影響。

內(nèi)存泄露

對(duì)C程序員來說這中情況是很常見的。

思考一下一些簡(jiǎn)單的類是如何堅(jiān)持訪問規(guī)則的:

class Guard {
    private int ACCESS_ALLOWED = 1;

    public boolean giveAccess() {
        return 42 == ACCESS_ALLOWED;
    }
}

客戶端代碼是非常安全的,調(diào)用giveAccess()檢查訪問規(guī)則。不幸的是對(duì)所有的客戶端代碼,它總是返回false。只有特權(quán)用戶在某種程度上可以改變ACCESS_ALLOWED常量并且獲得訪問權(quán)限。

事實(shí)上,這不是真的。這是證明它的代碼:

Guard guard = new Guard();
guard.giveAccess();   // false, no access

// bypass
Unsafe unsafe = getUnsafe();
Field f = guard.getClass().getDeclaredField("ACCESS_ALLOWED");
unsafe.putInt(guard, unsafe.objectFieldOffset(f), 42); // memory corruption

guard.giveAccess(); // true, access granted

現(xiàn)在所有的客戶端都沒有訪問限制了。

事實(shí)上同樣的功能也可以通過反射來實(shí)現(xiàn)。但有趣的是, 通過上面的方式我們修改任何對(duì)象,即使我們沒有持有對(duì)象的引用。

舉個(gè)例子, 在內(nèi)存中有另外的一個(gè)Guard對(duì)象,并且地址緊挨著當(dāng)前對(duì)象的地址,我們就可以通過下面的代碼來修改該對(duì)象的ACCESS_ALLOWED字段的值。

unsafe.putInt(guard, 16 + unsafe.objectFieldOffset(f), 42); // memory corruption

注意,我們沒有使用任何指向該對(duì)象的引用,16是Guard對(duì)象在32位架構(gòu)上的大小。我們也可以通過sizeOf方法來計(jì)算Guard對(duì)象的大小。

sizeOf

使用objectFieldOffset方法我們可以實(shí)現(xiàn)C風(fēng)格的sizeof方法。下面的方法實(shí)現(xiàn)返回對(duì)象的表面上的大小

public static long sizeOf(Object o) {
    Unsafe u = getUnsafe();
    HashSet<Field> fields = new HashSet<Field>();
    Class c = o.getClass();
    while (c != Object.class) {
        for (Field f : c.getDeclaredFields()) {
            if ((f.getModifiers() & Modifier.STATIC) == 0) {
                fields.add(f);
            }
        }
        c = c.getSuperclass();
    }

    // get offset
    long maxSize = 0;
    for (Field f : fields) {
        long offset = u.objectFieldOffset(f);
        if (offset > maxSize) {
            maxSize = offset;
        }
    }

    return ((maxSize/8) + 1) * 8;   // padding
}

算法邏輯如下:收集所有包括父類在內(nèi)的非靜態(tài)字段,獲得每個(gè)字段的偏移量,發(fā)現(xiàn)最大并添加填充。也許,我錯(cuò)過了一些東西,但是概念是明確的。

更簡(jiǎn)單的sizeof方法實(shí)現(xiàn)邏輯是:我們只讀取該對(duì)象對(duì)應(yīng)的class對(duì)象中關(guān)于大小的字段值。在JVM 1.7 32 位版本上該表示大小的字段偏移量是12。

public static long sizeOf(Object object){
    return getUnsafe().getAddress(
        normalize(getUnsafe().getInt(object, 4L)) + 12L);
}

normalize是一個(gè)將有符號(hào)的int類型轉(zhuǎn)為無符號(hào)的long類型的方法。

private static long normalize(int value) {
    if(value >= 0) return value;
    return (~0L >>> 32) & value;
}

太棒了,這個(gè)方法返回的結(jié)果和我們之前的sizeof函數(shù)是相同的。

but it requires specifyng agent option in your JVM.

事實(shí)上,對(duì)于合適的,安全的,準(zhǔn)確的sizeof函數(shù)最好使用java.lang.instrument包,但它需要特殊的JVM參數(shù)。

淺拷貝

在實(shí)現(xiàn)了計(jì)算對(duì)象淺層大小的基礎(chǔ)上,我們可以非常容易的添加對(duì)象的拷貝方法。標(biāo)準(zhǔn)的辦法需要修改我們的代碼和Cloneable。或者你可以實(shí)現(xiàn)自定義的對(duì)象拷貝函數(shù),但它不會(huì)變?yōu)橥ㄓ玫暮瘮?shù)。

淺拷貝:

static Object shallowCopy(Object obj) {
    long size = sizeOf(obj);
    long start = toAddress(obj);
    long address = getUnsafe().allocateMemory(size);
    getUnsafe().copyMemory(start, address, size);
    return fromAddress(address);
}

toAddressfromAddress 將對(duì)象轉(zhuǎn)為它在內(nèi)存中的地址或者從指定的地址內(nèi)容轉(zhuǎn)為對(duì)象。

static long toAddress(Object obj) {
    Object[] array = new Object[] {obj};
    long baseOffset = getUnsafe().arrayBaseOffset(Object[].class);
    return normalize(getUnsafe().getInt(array, baseOffset));
}

static Object fromAddress(long address) {
    Object[] array = new Object[] {null};
    long baseOffset = getUnsafe().arrayBaseOffset(Object[].class);
    getUnsafe().putLong(array, baseOffset, address);
    return array[0];
}

該拷貝函數(shù)可以用來拷貝任何類型的對(duì)象,因?yàn)閷?duì)象的大小是動(dòng)態(tài)計(jì)算的。

注意 在完成拷貝動(dòng)作后你需要將拷貝對(duì)象的類型強(qiáng)轉(zhuǎn)為目標(biāo)類型。

隱藏密碼

在Unsafe的直接內(nèi)存訪問方法使用case中有一個(gè)非常有趣的用法就是刪除內(nèi)存中不想要的對(duì)象。

大多數(shù)獲取用戶密碼的API方法的返回值不是byte[]就是char[],這是為什么呢?

這完全是出于安全原因, 因?yàn)槲覀兛梢栽诓恍枰鼈兊臅r(shí)候?qū)?shù)組元素置為失效。如果我們獲取的密碼是字符串類型,則密碼字符串是作為一個(gè)對(duì)象保存在內(nèi)存中的。要將該密碼字符串置為無效,我們只能講字符串引用職位null,但是該字符串的內(nèi)容任然存在內(nèi)存直到GC回收該對(duì)象后。

這個(gè)技巧在內(nèi)存創(chuàng)建一個(gè)假的大小相同字符串對(duì)象來替換原來的:

String password = new String("l00k@myHor$e");
String fake = new String(password.replaceAll(".", "?"));
System.out.println(password); // l00k@myHor$e
System.out.println(fake); // ????????????

getUnsafe().copyMemory(
          fake, 0L, null, toAddress(password), sizeOf(password));

System.out.println(password); // ????????????
System.out.println(fake); // ????????????

感覺安全了嗎?

其實(shí)該方法不是真的安全。想要真的安全我們可以通過反射API將字符串對(duì)象中的字符數(shù)組value字段的值修改為null。

Field stringValue = String.class.getDeclaredField("value");
stringValue.setAccessible(true);
char[] mem = (char[]) stringValue.get(password);
for (int i=0; i < mem.length; i++) {
  mem[i] = '?';
}

多重繼承

在Java中本來是沒有多重集成的。除非我們可以將任意的類型轉(zhuǎn)為我們想要的任意類型。

long intClassAddress = normalize(getUnsafe().getInt(new Integer(0), 4L));
long strClassAddress = normalize(getUnsafe().getInt("", 4L));
getUnsafe().putAddress(intClassAddress + 36, strClassAddress);

這段代碼將String類添加到Integer的超類集合中,所以我們的強(qiáng)轉(zhuǎn)代碼是沒有運(yùn)行時(shí)異常的。

(String) (Object) (new Integer(666))

有個(gè)問題是我們需要先將要轉(zhuǎn)的對(duì)象轉(zhuǎn)為Object,然后再轉(zhuǎn)為我們想要的類型。這是為了欺騙編譯器。

動(dòng)態(tài)類

We can create classes in runtime, for example from compiled .class file. To perform that read class contents to byte array and pass it properly to defineClass method.

我們可以在運(yùn)行時(shí)創(chuàng)建類, 例如通過一個(gè)編譯好的class文件。將class文件的內(nèi)容讀入到字節(jié)數(shù)組中然后將該數(shù)組傳遞到合適的defineClass方法中。

byte[] classContents = getClassContent();
Class c = getUnsafe().defineClass(
              null, classContents, 0, classContents.length);
    c.getMethod("a").invoke(c.newInstance(), null); // 1

讀取class文件內(nèi)如的代碼:

private static byte[] getClassContent() throws Exception {
    File f = new File("/home/mishadoff/tmp/A.class");
    FileInputStream input = new FileInputStream(f);
    byte[] content = new byte[(int)f.length()];
    input.read(content);
    input.close();
    return content;
}

該方式是非常有用的,如果你確實(shí)需要在運(yùn)行時(shí)動(dòng)態(tài)的創(chuàng)建類。比如生產(chǎn)代理類或切面類。

拋出一個(gè)異常

不喜歡受檢異常?這不是問題。

getUnsafe().throwException(new IOException());

該方法拋出一個(gè)受檢異常,但是你的代碼不需要強(qiáng)制捕獲該異常就像運(yùn)行時(shí)異常一樣。

快速序列化

這種使用方式更實(shí)用。

每個(gè)人都知道java標(biāo)準(zhǔn)的序列化的功能速度很慢而且它還需要類擁有公有的構(gòu)造函數(shù)。

外部序列化是更好的方式,但是需要定義針對(duì)待序列化類的schema。

非常流行的高性能序列化庫(kù),像kryo是有使用限制的,比如在內(nèi)存缺乏的環(huán)境就不合適。

但通過使用Unsafe類我們可以非常簡(jiǎn)單的實(shí)現(xiàn)完整的序列化功能。

序列化

  • 通過反射定義類的序列化。 這個(gè)可以只做一次。
  • 通過Unsafe的getLong, getInt, getObject等方法獲取字段真實(shí)的值。
  • 添加可以恢復(fù)該對(duì)象的標(biāo)識(shí)符。
  • 將這些數(shù)據(jù)寫入到輸出

當(dāng)然也可以使用壓縮來節(jié)省空間。

反序列化:

  • 創(chuàng)建一個(gè)序列化類的實(shí)例,可以通過方法allocateInstance。因?yàn)樵摲椒ú恍枰魏螛?gòu)造方法。
  • 創(chuàng)建schama, 和序列化類似
  • 從文件或輸入讀取或有的字段
  • 使用 UnsafeputLong, putInt, putObject等方法來填充對(duì)象。

Actually, there are much more details in correct inplementation, but intuition is clear.

事實(shí)上要正確實(shí)現(xiàn)序列化和反序列化需要注意很多細(xì)節(jié),但是思路是清晰的。

這種序列化方式是非常快的。

順便說一句,在 kryo 有許多使用Unsafe的嘗試 http://code.google.com/p/kryo/issues/detail?id=75

大數(shù)組

如你所知Java數(shù)組長(zhǎng)度的最大值是Integer.MAX_VALUE。使用直接內(nèi)存分配我們可以創(chuàng)建非常大的數(shù)組,該數(shù)組的大小只受限于堆的大小。

這里有一個(gè)SuperArray的實(shí)現(xiàn):

class SuperArray {
    private final static int BYTE = 1;

    private long size;
    private long address;

    public SuperArray(long size) {
        this.size = size;
        address = getUnsafe().allocateMemory(size * BYTE);
    }

    public void set(long i, byte value) {
        getUnsafe().putByte(address + i * BYTE, value);
    }

    public int get(long idx) {
        return getUnsafe().getByte(address + idx * BYTE);
    }

    public long size() {
        return size;
    }
}

一個(gè)簡(jiǎn)單的用法:

long SUPER_SIZE = (long)Integer.MAX_VALUE * 2;
SuperArray array = new SuperArray(SUPER_SIZE);
System.out.println("Array size:" + array.size()); // 4294967294
for (int i = 0; i < 100; i++) {
    array.set((long)Integer.MAX_VALUE + i, (byte)3);
    sum += array.get((long)Integer.MAX_VALUE + i);
}
System.out.println("Sum of 100 elements:" + sum);  // 300

事實(shí)上該技術(shù)使用了非堆內(nèi)存off-heap memory,在 java.nio 包中也有使用。

通過這種方式分配的內(nèi)存不在堆上,并且不受GC管理。因此需要小心使用Unsafe.freeMemory()。該方法不會(huì)做任何邊界檢查,因此任何不合法的訪問可能就會(huì)導(dǎo)致JVM奔潰。

這種使用方式對(duì)于數(shù)學(xué)計(jì)算是非常有用的,因?yàn)榇a可以操作非常大的數(shù)據(jù)數(shù)組。 同樣的編寫實(shí)時(shí)程序的程序員對(duì)此也非常感興趣,因?yàn)椴皇蹽C限制,就不會(huì)因?yàn)镚C導(dǎo)致非常大的停頓。

并發(fā)

關(guān)于并發(fā)編程使用Unsafe的只言片語(yǔ)。compareAndSwap 方法是原子的,可以用來實(shí)現(xiàn)高性能的無鎖化數(shù)據(jù)結(jié)構(gòu)。

舉個(gè)例子,多個(gè)線程并發(fā)的更新共享的對(duì)象這種場(chǎng)景:

首先我們定義一個(gè)簡(jiǎn)單的接口 Counter:

interface Counter {
    void increment();
    long getCounter();
}

我們定義工作線程 CounterClient, 它會(huì)使用 Counter:

class CounterClient implements Runnable {
    private Counter c;
    private int num;

    public CounterClient(Counter c, int num) {
        this.c = c;
        this.num = num;
    }

    @Override
    public void run() {
        for (int i = 0; i < num; i++) {
            c.increment();
        }
    }
}

這是測(cè)試代碼:

int NUM_OF_THREADS = 1000;
int NUM_OF_INCREMENTS = 100000;
ExecutorService service = Executors.newFixedThreadPool(NUM_OF_THREADS);
Counter counter = ... // creating instance of specific counter
long before = System.currentTimeMillis();
for (int i = 0; i < NUM_OF_THREADS; i++) {
    service.submit(new CounterClient(counter, NUM_OF_INCREMENTS));
}
service.shutdown();
service.awaitTermination(1, TimeUnit.MINUTES);
long after = System.currentTimeMillis();
System.out.println("Counter result: " + c.getCounter());
System.out.println("Time passed in ms:" + (after - before));

第一個(gè)實(shí)現(xiàn)-沒有同步的計(jì)數(shù)器

class StupidCounter implements Counter {
    private long counter = 0;

    @Override
    public void increment() {
        counter++;
    }

    @Override
    public long getCounter() {
        return counter;
    }
}

Output:

Counter result: 99542945
Time passed in ms: 679

速度很多,但是沒有對(duì)所有的線程進(jìn)行協(xié)調(diào)所以結(jié)果是錯(cuò)誤的。第二個(gè)版本,使用Java常見的同步方式來實(shí)現(xiàn)

class SyncCounter implements Counter {
    private long counter = 0;

    @Override
    public synchronized void increment() {
        counter++;
    }

    @Override
    public long getCounter() {
        return counter;
    }
}

Output:

Counter result: 100000000
Time passed in ms: 10136

徹底的同步當(dāng)然會(huì)導(dǎo)致正確的結(jié)果。但是花費(fèi)的時(shí)間令人沮喪。讓我們?cè)囋?ReentrantReadWriteLock:

class LockCounter implements Counter {
    private long counter = 0;
    private WriteLock lock = new ReentrantReadWriteLock().writeLock();

    @Override
    public void increment() {
        lock.lock();
        counter++;
        lock.unlock();
    }

    @Override
    public long getCounter() {
        return counter;
    }
}

Output:

Counter result: 100000000
Time passed in ms: 8065

結(jié)果依然是正確的,時(shí)間也短。那使用原子的類呢?

class AtomicCounter implements Counter {
    AtomicLong counter = new AtomicLong(0);

    @Override
    public void increment() {
        counter.incrementAndGet();
    }

    @Override
    public long getCounter() {
        return counter.get();
    }
}

Output:

Counter result: 100000000
Time passed in ms: 6552

使用AtomicCounter的效果更好一點(diǎn)。最后我們?cè)囋?code>Unsafe的原子方法compareAndSwapLong看看是不是更進(jìn)一步。

class CASCounter implements Counter {
    private volatile long counter = 0;
    private Unsafe unsafe;
    private long offset;

    public CASCounter() throws Exception {
        unsafe = getUnsafe();
        offset = unsafe.objectFieldOffset(CASCounter.class.getDeclaredField("counter"));
    }

    @Override
    public void increment() {
        long before = counter;
        while (!unsafe.compareAndSwapLong(this, offset, before, before + 1)) {
            before = counter;
        }
    }

    @Override
    public long getCounter() {
        return counter;
    }
}

Output:

Counter result: 100000000
Time passed in ms: 6454

開起來和使用原子類是一樣的效果,難道原子類使用了Unsafe?答案是YES。

事實(shí)上該例子非常簡(jiǎn)單但表現(xiàn)出了Unsafe的強(qiáng)大功能。

就像前面提到的 CAS原語(yǔ)可以用來實(shí)現(xiàn)高效的無鎖數(shù)據(jù)結(jié)構(gòu)。實(shí)現(xiàn)的原理很簡(jiǎn)單:

  • 擁有一個(gè)狀態(tài)
  • 創(chuàng)建一個(gè)它的副本
  • 修改該副本
  • 執(zhí)行 CAS 操作
  • 如果失敗就重復(fù)執(zhí)行

事實(shí)上,在真實(shí)的環(huán)境它的實(shí)現(xiàn)難度超過你的想象,這其中有需要類似ABA,指令重排序這樣的問題。

如果你確實(shí)對(duì)此感興趣,你可以參考關(guān)于無鎖HashMap的精彩演示。

Bonus

Documentation for park method from Unsafe class contains longest English sentence I’ve ever seen:

Block current thread, returning when a balancing unpark occurs, or a balancing 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 unpark is, so it would be strange to place it elsewhere.

結(jié)論

盡管Unsafe有這么多有用的應(yīng)用,但是盡力不要使用。當(dāng)然了使用JDK中利用了Unsafe實(shí)現(xiàn)的類是可以的。或者你對(duì)你代碼功力非常自信。

參考地址

如果大家喜歡我的文章,可以關(guān)注個(gè)人訂閱號(hào)。歡迎隨時(shí)留言、交流。如果想加入微信群的話一起討論的話,請(qǐng)加管理員簡(jiǎn)棧文化-小助手(lastpass4u),他會(huì)拉你們進(jìn)群。

簡(jiǎn)棧文化服務(wù)訂閱號(hào)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評(píng)論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,716評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評(píng)論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,706評(píng)論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,036評(píng)論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評(píng)論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,203評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,725評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,451評(píng)論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,677評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評(píng)論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,857評(píng)論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評(píng)論 1 295
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,407評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,643評(píng)論 2 380

推薦閱讀更多精彩內(nèi)容

  • Unsafe是位于sun.misc包下的一個(gè)類,主要提供一些用于執(zhí)行低級(jí)別、不安全操作的方法,如直接訪問系統(tǒng)內(nèi)存資...
    Harri2012閱讀 486評(píng)論 0 1
  • 問題 (1)Unsafe是什么? (2)Unsafe只有CAS的功能嗎? (3)Unsafe為什么是不安全的? (...
    架構(gòu)師Javaspring閱讀 296評(píng)論 0 1
  • JAVA序列化機(jī)制的深入研究 對(duì)象序列化的最主要的用處就是在傳遞,和保存對(duì)象(object)的時(shí)候,保證對(duì)象的完整...
    時(shí)待吾閱讀 10,907評(píng)論 0 24
  • 問題 (1)Unsafe是什么? (2)Unsafe只有CAS的功能嗎? (3)Unsafe為什么是不安全的? (...
    編程小世界閱讀 726評(píng)論 0 0
  • 圈圈,你應(yīng)該學(xué)會(huì)享受孤獨(dú),開心的活著,謝謝這幾天一直陪我的人,出差就不會(huì)顯得那么孤獨(dú)了! 心情很差,在不斷的用正能...
    OO碰到OO閱讀 140評(píng)論 0 0