Java魔法類:Unsafe應用解析

Unsafe是位于sun.misc包下的一個類,主要提供一些用于執(zhí)行低級別、不安全操作的方法煞烫,如直接訪問系統(tǒng)內存資源、自主管理內存資源等累颂,這些方法在提升Java運行效率滞详、增強Java語言底層資源操作能力方面起到了很大的作用凛俱。但由于Unsafe類使Java語言擁有了類似C語言指針一樣操作內存空間的能力,這無疑也增加了程序發(fā)生相關指針問題的風險料饥。在程序中過度蒲犬、不正確使用Unsafe類會使得程序出錯的概率變大,使得Java這種安全的語言變得不再“安全”岸啡,因此對Unsafe的使用一定要慎重原叮。

注:本文對sun.misc.Unsafe公共API功能及相關應用場景進行介紹。

基本介紹

如下Unsafe源碼所示巡蘸,Unsafe類為一單例實現(xiàn)奋隶,提供靜態(tài)方法getUnsafe獲取Unsafe實例,當且僅當調用getUnsafe方法的類為引導類加載器所加載時才合法悦荒,否則拋出SecurityException異常唯欣。

public final class Unsafe {
  // 單例對象
  private static final Unsafe theUnsafe;

  private Unsafe() {
  }
  @CallerSensitive
  public static Unsafe getUnsafe() {
    Class var0 = Reflection.getCallerClass();
    // 僅在引導類加載器`BootstrapClassLoader`加載時才合法
    if(!VM.isSystemDomainLoader(var0.getClassLoader())) {    
      throw new SecurityException("Unsafe");
    } else {
      return theUnsafe;
    }
  }
}

那如若想使用這個類,該如何獲取其實例搬味?有如下兩個可行方案境氢。

其一碰纬,從getUnsafe方法的使用限制條件出發(fā)脐区,通過Java命令行命令-Xbootclasspath/a把調用Unsafe相關方法的類A所在jar包路徑追加到默認的bootstrap路徑中炕柔,使得A被引導類加載器加載陵刹,從而通過Unsafe.getUnsafe方法安全的獲取Unsafe實例衰琐。

java -Xbootclasspath/a: ${path}   // 其中path為調用Unsafe相關方法的類所在jar包路徑 

其二,通過反射獲取單例對象theUnsafe狗热。

private static Unsafe reflectGetUnsafe() {
    try {
      Field field = Unsafe.class.getDeclaredField("theUnsafe");
      field.setAccessible(true);
      return (Unsafe) field.get(null);
    } catch (Exception e) {
      log.error(e.getMessage(), e);
      return null;
    }
}

功能介紹

image

如上圖所示熟丸,Unsafe提供的API大致可分為內存操作隙弛、CAS、Class相關总珠、對象操作、線程調度、系統(tǒng)信息獲取、內存屏障唐责、數(shù)組操作等幾類,下面將對其相關方法和應用場景進行詳細介紹。

內存操作

這部分主要包含堆外內存的分配于颖、拷貝榨崩、釋放、給定地址值操作等方法。

//分配內存, 相當于C++的malloc函數(shù)
public native long allocateMemory(long bytes);
//擴充內存
public native long reallocateMemory(long address, long bytes);
//釋放內存
public native void freeMemory(long address);
//在給定的內存塊中設置值
public native void setMemory(Object o, long offset, long bytes, byte value);
//內存拷貝
public native void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes);
//獲取給定地址值,忽略修飾限定符的訪問限制违帆。與此類似操作還有: getInt渊抄,getDouble含衔,getLong,getChar等
public native Object getObject(Object o, long offset);
//為給定地址設置值强经,忽略修飾限定符的訪問限制,與此類似操作還有: putInt,putDouble汁果,putLong鳄乏,putChar等
public native void putObject(Object o, long offset, Object x);
//獲取給定地址的byte類型的值(當且僅當該內存地址為allocateMemory分配時善玫,此方法結果為確定的)
public native byte getByte(long address);
//為給定地址設置byte類型的值(當且僅當該內存地址為allocateMemory分配時,此方法結果才是確定的)
public native void putByte(long address, byte x);

通常,我們在Java中創(chuàng)建的對象都處于堆內內存(heap)中,堆內內存是由JVM所管控的Java進程內存,并且它們遵循JVM的內存管理機制,JVM會采用垃圾回收機制統(tǒng)一管理堆內存。與之相對的是堆外內存,存在于JVM管控之外的內存區(qū)域,Java中對堆外內存的操作,依賴于Unsafe提供的操作堆外內存的native方法。

使用堆外內存的原因

  • 對垃圾回收停頓的改善。由于堆外內存是直接受操作系統(tǒng)管理而不是JVM狞悲,所以當我們使用堆外內存時荸恕,即可保持較小的堆內內存規(guī)模算撮。從而在GC時減少回收停頓對于應用的影響审洞。
  • 提升程序I/O操作的性能南吮。通常在I/O通信過程中誊酌,會存在堆內內存到堆外內存的數(shù)據(jù)拷貝操作部凑,對于需要頻繁進行內存間數(shù)據(jù)拷貝且生命周期較短的暫存數(shù)據(jù),都建議存儲到堆外內存术辐。

典型應用

DirectByteBuffer是Java用于實現(xiàn)堆外內存的一個重要類砚尽,通常用在通信過程中做緩沖池,如在Netty辉词、MINA等NIO框架中應用廣泛。DirectByteBuffer對于堆外內存的創(chuàng)建猾骡、使用瑞躺、銷毀等邏輯均由Unsafe提供的堆外內存API來實現(xiàn)敷搪。

下圖為DirectByteBuffer構造函數(shù),創(chuàng)建DirectByteBuffer的時候幢哨,通過Unsafe.allocateMemory分配內存赡勘、Unsafe.setMemory進行內存初始化,而后構建Cleaner對象用于跟蹤DirectByteBuffer對象的垃圾回收捞镰,以實現(xiàn)當DirectByteBuffer被垃圾回收時闸与,分配的堆外內存一起被釋放。

image

那么如何通過構建垃圾回收追蹤對象Cleaner實現(xiàn)堆外內存釋放呢岸售?

Cleaner繼承自Java四大引用類型之一的虛引用PhantomReference(眾所周知践樱,無法通過虛引用獲取與之關聯(lián)的對象實例,且當對象僅被虛引用引用時凸丸,在任何發(fā)生GC的時候拷邢,其均可被回收),通常PhantomReference與引用隊列ReferenceQueue結合使用屎慢,可以實現(xiàn)虛引用關聯(lián)對象被垃圾回收時能夠進行系統(tǒng)通知瞭稼、資源清理等功能。如下圖所示腻惠,當某個被Cleaner引用的對象將被回收時环肘,JVM垃圾收集器會將此對象的引用放入到對象引用中的pending鏈表中,等待Reference-Handler進行相關處理集灌。其中廷臼,Reference-Handler為一個擁有最高優(yōu)先級的守護線程,會循環(huán)不斷的處理pending鏈表中的對象引用绝页,執(zhí)行Cleaner的clean方法進行相關清理工作荠商。

image

所以當DirectByteBuffer僅被Cleaner引用(即為虛引用)時,其可以在任意GC時段被回收续誉。當DirectByteBuffer實例對象被回收時莱没,在Reference-Handler線程操作中,會調用Cleaner的clean方法根據(jù)創(chuàng)建Cleaner時傳入的Deallocator來進行堆外內存的釋放酷鸦。

image

CAS相關

如下源代碼釋義所示饰躲,這部分主要為CAS相關操作的方法。

/**
    *  CAS
  * @param o         包含要修改field的對象
  * @param offset    對象中某field的偏移量
  * @param expected  期望值
  * @param update    更新值
  * @return          true | false
  */
public final native boolean compareAndSwapObject(Object o, long offset,  Object expected, Object update);

public final native boolean compareAndSwapInt(Object o, long offset, int expected,int update);

public final native boolean compareAndSwapLong(Object o, long offset, long expected, long update);

什么是CAS? 即比較并替換臼隔,實現(xiàn)并發(fā)算法時常用到的一種技術嘹裂。CAS操作包含三個操作數(shù)——內存位置、預期原值及新值摔握。執(zhí)行CAS操作的時候寄狼,將內存位置的值與預期原值比較,如果相匹配,那么處理器會自動將該位置值更新為新值泊愧,否則伊磺,處理器不做任何操作。我們都知道删咱,CAS是一條CPU的原子指令(cmpxchg指令)屑埋,不會造成所謂的數(shù)據(jù)不一致問題,Unsafe提供的CAS方法(如compareAndSwapXXX)底層實現(xiàn)即為CPU指令cmpxchg。

典型應用

CAS在java.util.concurrent.atomic相關類、Java AQS、CurrentHashMap等實現(xiàn)上有非常廣泛的應用。如下圖所示,AtomicInteger的實現(xiàn)中,靜態(tài)字段valueOffset即為字段value的內存偏移地址镇饮,valueOffset的值在AtomicInteger初始化時钙勃,在靜態(tài)代碼塊中通過Unsafe的objectFieldOffset方法獲取。在AtomicInteger中提供的線程安全方法中跛十,通過字段valueOffset的值可以定位到AtomicInteger對象中value的內存地址奈偏,從而可以根據(jù)CAS實現(xiàn)對value字段的原子操作。

image

下圖為某個AtomicInteger對象自增操作前后的內存示意圖枉证,對象的基地址baseAddress=“0x110000”秒赤,通過baseAddress+valueOffset得到value的內存地址valueAddress=“0x11000c”;然后通過CAS進行原子性的更新操作,成功則返回盆均,否則繼續(xù)重試饰抒,直到更新成功為止。

image

線程調度

這部分郁轻,包括線程掛起输吏、恢復、鎖機制等方法躲查。


//取消阻塞線程
public native void unpark(Object thread);
//阻塞線程
public native void park(boolean isAbsolute, long time);
//獲得對象鎖(可重入鎖)
@Deprecated
public native void monitorEnter(Object o);
//釋放對象鎖
@Deprecated
public native void monitorExit(Object o);
//嘗試獲取對象鎖
@Deprecated
public native boolean tryMonitorEnter(Object o);

如上源碼說明中它浅,方法park、unpark即可實現(xiàn)線程的掛起與恢復镣煮,將一個線程進行掛起是通過park方法實現(xiàn)的姐霍,調用park方法后,線程將一直阻塞直到超時或者中斷等條件出現(xiàn)典唇;unpark可以終止一個掛起的線程镊折,使其恢復正常。

典型應用

Java鎖和同步器框架的核心類AbstractQueuedSynchronizer介衔,就是通過調用LockSupport.park()LockSupport.unpark()實現(xiàn)線程的阻塞和喚醒的恨胚,而LockSupport的park、unpark方法實際是調用Unsafe的park炎咖、unpark方式來實現(xiàn)赃泡。

Class相關

此部分主要提供Class和它的靜態(tài)字段的操作相關方法寒波,包含靜態(tài)字段內存定位、定義類升熊、定義匿名類俄烁、檢驗&確保初始化等。

//獲取給定靜態(tài)字段的內存地址偏移量级野,這個值對于給定的字段是唯一且固定不變的
public native long staticFieldOffset(Field f);
//獲取一個靜態(tài)類中給定字段的對象指針
public native Object staticFieldBase(Field f);
//判斷是否需要初始化一個類页屠,通常在獲取一個類的靜態(tài)屬性的時候(因為一個類如果沒初始化,它的靜態(tài)屬性也不會初始化)使用勺阐。 當且僅當ensureClassInitialized方法不生效時返回false卷中。
public native boolean shouldBeInitialized(Class<?> c);
//檢測給定的類是否已經初始化矛双。通常在獲取一個類的靜態(tài)屬性的時候(因為一個類如果沒初始化渊抽,它的靜態(tài)屬性也不會初始化)使用。
public native void ensureClassInitialized(Class<?> c);
//定義一個類议忽,此方法會跳過JVM的所有安全檢查懒闷,默認情況下,ClassLoader(類加載器)和ProtectionDomain(保護域)實例來源于調用者
public native Class<?> defineClass(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain);
//定義一個匿名類
public native Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches);

典型應用

從Java 8開始栈幸,JDK使用invokedynamic及VM Anonymous Class結合來實現(xiàn)Java語言層面上的Lambda表達式愤估。

  • invokedynamic: invokedynamic是Java 7為了實現(xiàn)在JVM上運行動態(tài)語言而引入的一條新的虛擬機指令,它可以實現(xiàn)在運行期動態(tài)解析出調用點限定符所引用的方法速址,然后再執(zhí)行該方法玩焰,invokedynamic指令的分派邏輯是由用戶設定的引導方法決定。
  • VM Anonymous Class:可以看做是一種模板機制芍锚,針對于程序動態(tài)生成很多結構相同昔园、僅若干常量不同的類時,可以先創(chuàng)建包含常量占位符的模板類并炮,而后通過Unsafe.defineAnonymousClass方法定義具體類時填充模板的占位符生成具體的匿名類默刚。生成的匿名類不顯式掛在任何ClassLoader下面,只要當該類沒有存在的實例對象逃魄、且沒有強引用來引用該類的Class對象時荤西,該類就會被GC回收。故而VM Anonymous Class相比于Java語言層面的匿名內部類無需通過ClassClassLoader進行類加載且更易回收伍俘。

在Lambda表達式實現(xiàn)中邪锌,通過invokedynamic指令調用引導方法生成調用點,在此過程中癌瘾,會通過ASM動態(tài)生成字節(jié)碼觅丰,而后利用Unsafe的defineAnonymousClass方法定義實現(xiàn)相應的函數(shù)式接口的匿名類,然后再實例化此匿名類柳弄,并返回與此匿名類中函數(shù)式方法的方法句柄關聯(lián)的調用點舶胀;而后可以通過此調用點實現(xiàn)調用相應Lambda表達式定義邏輯的功能概说。下面以如下圖所示的Test類來舉例說明。

image

Test類編譯后的class文件反編譯后的結果如下圖一所示(刪除了對本文說明無意義的部分)嚣伐,我們可以從中看到main方法的指令實現(xiàn)糖赔、invokedynamic指令調用的引導方法BootstrapMethods、及靜態(tài)方法lambda$main$0(實現(xiàn)了Lambda表達式中字符串打印邏輯)等轩端。在引導方法執(zhí)行過程中放典,會通過Unsafe.defineAnonymousClass生成如下圖二所示的實現(xiàn)Consumer接口的匿名類。其中基茵,accept方法通過調用Test類中的靜態(tài)方法lambda$main$0來實現(xiàn)Lambda表達式中定義的邏輯奋构。而后執(zhí)行語句consumer.accept("lambda")其實就是調用下圖二所示的匿名類的accept方法。

image

對象操作

此部分主要包含對象成員屬性相關操作及非常規(guī)的對象實例化方式等相關方法拱层。

//返回對象成員屬性在內存地址相對于此對象的內存地址的偏移量
public native long objectFieldOffset(Field f);
//獲得給定對象的指定地址偏移量的值弥臼,與此類似操作還有:getInt忙芒,getDouble胰苏,getLong,getChar等
public native Object getObject(Object o, long offset);
//給定對象的指定地址偏移量設值姜胖,與此類似操作還有:putInt烙肺,putDouble纳猪,putLong,putChar等
public native void putObject(Object o, long offset, Object x);
//從對象的指定偏移量處獲取變量的引用桃笙,使用volatile的加載語義
public native Object getObjectVolatile(Object o, long offset);
//存儲變量的引用到對象的指定的偏移量處氏堤,使用volatile的存儲語義
public native void putObjectVolatile(Object o, long offset, Object x);
//有序、延遲版本的putObjectVolatile方法搏明,不保證值的改變被其他線程立即看到鼠锈。只有在field被volatile修飾符修飾時有效
public native void putOrderedObject(Object o, long offset, Object x);
//繞過構造方法、初始化代碼來創(chuàng)建對象
public native Object allocateInstance(Class<?> cls) throws InstantiationException;

典型應用

  • 常規(guī)對象實例化方式:我們通常所用到的創(chuàng)建對象的方式熏瞄,從本質上來講脚祟,都是通過new機制來實現(xiàn)對象的創(chuàng)建。但是强饮,new機制有個特點就是當類只提供有參的構造函數(shù)且無顯示聲明無參構造函數(shù)時由桌,則必須使用有參構造函數(shù)進行對象構造,而使用有參構造函數(shù)時邮丰,必須傳遞相應個數(shù)的參數(shù)才能完成對象實例化行您。
  • 非常規(guī)的實例化方式:而Unsafe中提供allocateInstance方法,僅通過Class對象就可以創(chuàng)建此類的實例對象剪廉,而且不需要調用其構造函數(shù)娃循、初始化代碼、JVM安全檢查等斗蒋。它抑制修飾符檢測捌斧,也就是即使構造器是private修飾的也能通過此方法實例化笛质,只需提類對象即可創(chuàng)建相應的對象。由于這種特性捞蚂,allocateInstance在java.lang.invoke妇押、Objenesis(提供繞過類構造器的對象生成方式)、Gson(反序列化時用到)中都有相應的應用姓迅。

如下圖所示敲霍,在Gson反序列化時,如果類有默認構造函數(shù)丁存,則通過反射調用默認構造函數(shù)創(chuàng)建實例肩杈,否則通過UnsafeAllocator來實現(xiàn)對象實例的構造,UnsafeAllocator通過調用Unsafe的allocateInstance實現(xiàn)對象的實例化解寝,保證在目標類無默認構造函數(shù)時扩然,反序列化不夠影響。

image

數(shù)組相關

這部分主要介紹與數(shù)據(jù)操作相關的arrayBaseOffset與arrayIndexScale這兩個方法编丘,兩者配合起來使用与学,即可定位數(shù)組中每個元素在內存中的位置。

//返回數(shù)組中第一個元素的偏移地址
public native int arrayBaseOffset(Class<?> arrayClass);
//返回數(shù)組中一個元素占用的大小
public native int arrayIndexScale(Class<?> arrayClass);

典型應用

這兩個與數(shù)據(jù)操作相關的方法嘉抓,在java.util.concurrent.atomic 包下的AtomicIntegerArray(可以實現(xiàn)對Integer數(shù)組中每個元素的原子性操作)中有典型的應用,如下圖AtomicIntegerArray源碼所示晕窑,通過Unsafe的arrayBaseOffset抑片、arrayIndexScale分別獲取數(shù)組首元素的偏移地址base及單個元素大小因子scale。后續(xù)相關原子性操作杨赤,均依賴于這兩個值進行數(shù)組中元素的定位敞斋,如下圖二所示的getAndAdd方法即通過checkedByteOffset方法獲取某數(shù)組元素的偏移地址,而后通過CAS實現(xiàn)原子性操作疾牲。

image

內存屏障

在Java 8中引入植捎,用于定義內存屏障(也稱內存柵欄,內存柵障阳柔,屏障指令等焰枢,是一類同步屏障指令,是CPU或編譯器在對內存隨機訪問的操作中的一個同步點舌剂,使得此點之前的所有讀寫操作都執(zhí)行后才可以開始執(zhí)行此點之后的操作)济锄,避免代碼重排序。

//內存屏障霍转,禁止load操作重排序荐绝。屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前
public native void loadFence();
//內存屏障避消,禁止store操作重排序低滩。屏障前的store操作不能被重排序到屏障后召夹,屏障后的store操作不能被重排序到屏障前
public native void storeFence();
//內存屏障,禁止load恕沫、store操作重排序
public native void fullFence();

典型應用

在Java 8中引入了一種鎖的新機制——StampedLock戳鹅,它可以看成是讀寫鎖的一個改進版本。StampedLock提供了一種樂觀讀鎖的實現(xiàn)昏兆,這種樂觀讀鎖類似于無鎖的操作枫虏,完全不會阻塞寫線程獲取寫鎖,從而緩解讀多寫少時寫線程“饑餓”現(xiàn)象爬虱。由于StampedLock提供的樂觀讀鎖不阻塞寫線程獲取讀鎖隶债,當線程共享變量從主內存load到線程工作內存時,會存在數(shù)據(jù)不一致問題跑筝,所以當使用StampedLock的樂觀讀鎖時死讹,需要遵從如下圖用例中使用的模式來確保數(shù)據(jù)的一致性。

image

如上圖用例所示計算坐標點Point對象曲梗,包含點移動方法move及計算此點到原點的距離的方法distanceFromOrigin赞警。在方法distanceFromOrigin中,首先虏两,通過tryOptimisticRead方法獲取樂觀讀標記愧旦;然后從主內存中加載點的坐標值 (x,y);而后通過StampedLock的validate方法校驗鎖狀態(tài)定罢,判斷坐標點(x,y)從主內存加載到線程工作內存過程中笤虫,主內存的值是否已被其他線程通過move方法修改,如果validate返回值為true祖凫,證明(x, y)的值未被修改琼蚯,可參與后續(xù)計算;否則惠况,需加悲觀讀鎖遭庶,再次從主內存加載(x,y)的最新值,然后再進行距離計算稠屠。其中峦睡,校驗鎖狀態(tài)這步操作至關重要,需要判斷鎖狀態(tài)是否發(fā)生改變完箩,從而判斷之前copy到線程工作內存中的值是否與主內存的值存在不一致赐俗。

下圖為StampedLock.validate方法的源碼實現(xiàn),通過鎖標記與相關常量進行位運算弊知、比較來校驗鎖狀態(tài)阻逮,在校驗邏輯之前,會通過Unsafe的loadFence方法加入一個load內存屏障秩彤,目的是避免上圖用例中步驟②和StampedLock.validate中鎖狀態(tài)校驗運算發(fā)生重排序導致鎖狀態(tài)校驗不準確的問題叔扼。

image

系統(tǒng)相關

這部分包含兩個獲取系統(tǒng)相關信息的方法事哭。

//返回系統(tǒng)指針的大小。返回值為4(32位系統(tǒng))或 8(64位系統(tǒng))瓜富。
public native int addressSize();  
//內存頁的大小鳍咱,此值為2的冪次方。
public native int pageSize();

典型應用

如下圖所示的代碼片段与柑,為java.nio下的工具類Bits中計算待申請內存所需內存頁數(shù)量的靜態(tài)方法谤辜,其依賴于Unsafe中pageSize方法獲取系統(tǒng)內存頁大小實現(xiàn)后續(xù)計算邏輯。

image

結語

本文對Java中的sun.misc.Unsafe的用法及應用場景進行了基本介紹价捧,我們可以看到Unsafe提供了很多便捷丑念、有趣的API方法。即便如此结蟋,由于Unsafe中包含大量自主操作內存的方法脯倚,如若使用不當,會對程序帶來許多不可控的災難嵌屎。因此對它的使用我們需要慎之又慎推正。

參考資料

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市宝惰,隨后出現(xiàn)的幾起案子植榕,更是在濱河造成了極大的恐慌,老刑警劉巖掌测,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件内贮,死亡現(xiàn)場離奇詭異,居然都是意外死亡汞斧,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門什燕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來粘勒,“玉大人,你說我怎么就攤上這事屎即∶硭” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵技俐,是天一觀的道長乘陪。 經常有香客問我,道長雕擂,這世上最難降的妖魔是什么啡邑? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮井赌,結果婚禮上谤逼,老公的妹妹穿的比我還像新娘贵扰。我一直安慰自己,他們只是感情好流部,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布戚绕。 她就那樣靜靜地躺著,像睡著了一般枝冀。 火紅的嫁衣襯著肌膚如雪舞丛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天果漾,我揣著相機與錄音球切,去河邊找鬼。 笑死跨晴,一個胖子當著我的面吹牛欧聘,可吹牛的內容都是我干的。 我是一名探鬼主播端盆,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼怀骤,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了焕妙?” 一聲冷哼從身側響起蒋伦,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎焚鹊,沒想到半個月后痕届,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡末患,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年研叫,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片璧针。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡嚷炉,死狀恐怖,靈堂內的尸體忽然破棺而出探橱,到底是詐尸還是另有隱情申屹,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布隧膏,位于F島的核電站哗讥,受9級特大地震影響,放射性物質發(fā)生泄漏胞枕。R本人自食惡果不足惜杆煞,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧索绪,春花似錦湖员、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至唤反,卻和暖如春凳寺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背彤侍。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工肠缨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人盏阶。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓晒奕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親名斟。 傳聞我的和親對象是個殘疾皇子脑慧,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內容