Java ClassLoader 透析

ClassLoader 是 Java 屆最為神秘的技術(shù)之一,無數(shù)人被它傷透了腦筋呵俏,摸不清門道究竟在哪里堆缘。網(wǎng)上的文章也是一篇又一篇,經(jīng)過本人的親自鑒定普碎,絕大部分內(nèi)容都是在誤導(dǎo)別人吼肥。本文我?guī)ёx者徹底吃透 ClassLoader,以后其它的相關(guān)文章你們可以不必再細(xì)看了

ClassLoader 做什么的麻车?

顧名思義缀皱,它是用來加載 Class 的。它負(fù)責(zé)將 Class 的字節(jié)碼形式轉(zhuǎn)換成內(nèi)存形式的 Class 對象动猬。字節(jié)碼可以來自于磁盤文件 *.class啤斗,也可以是 jar 包里的 *.class,也可以來自遠(yuǎn)程服務(wù)器提供的字節(jié)流枣察,字節(jié)碼的本質(zhì)就是一個字節(jié)數(shù)組 []byte争占,它有特定的復(fù)雜的內(nèi)部格式。

有很多字節(jié)碼加密技術(shù)就是依靠定制 ClassLoader 來實現(xiàn)的序目。先使用工具對字節(jié)碼文件進行加密臂痕,運行時使用定制的 ClassLoader 先解密文件內(nèi)容再加載這些解密后的字節(jié)碼。

每個 Class 對象的內(nèi)部都有一個 classLoader 字段來標(biāo)識自己是由哪個 ClassLoader 加載的猿涨。

class Class<T> {
  ...
  private final ClassLoader classLoader;
  ...
}

延遲加載

JVM 運行并不是一次性加載所需要的全部類的握童,它是按需加載,也就是延遲加載叛赚。程序在運行的過程中會逐漸遇到很多不認(rèn)識的新類澡绩,這時候就會調(diào)用 ClassLoader 來加載這些類。加載完成后就會將 Class 對象存在 ClassLoader 里面俺附,下次就不需要重新加載了肥卡。

比如你在調(diào)用某個類的靜態(tài)方法時,首先這個類肯定是需要被加載的事镣,但是并不會觸及這個類的實例字段步鉴,那么實例字段的類別 Class 就可以暫時不必去加載,但是它可能會加載靜態(tài)字段相關(guān)的類別璃哟,因為靜態(tài)方法會訪問靜態(tài)字段氛琢。而實例字段的類別需要等到你實例化對象的時候才可能會加載。

各司其職

JVM 運行實例中會存在多個 ClassLoader随闪,不同的 ClassLoader 會從不同的地方加載字節(jié)碼文件阳似。它可以從不同的文件目錄加載,也可以從不同的 jar 文件中加載铐伴,也可以從網(wǎng)絡(luò)上不同的服務(wù)地址來加載撮奏。

JVM 中內(nèi)置了三個重要的 ClassLoader俏讹,分別是 BootstrapClassLoaderExtensionClassLoaderAppClassLoader挽荡。

BootstrapClassLoader 負(fù)責(zé)加載 JVM 運行時核心類藐石,這些類位于 JAVA_HOME/lib/rt.jar 文件中,我們常用內(nèi)置庫 java.xxx.* 都在里面定拟,比如 java.util.于微、java.io.、java.nio.青自、java.lang. 等等株依。這個 ClassLoader 比較特殊,它是由 C 代碼實現(xiàn)的延窜,我們將它稱之為「根加載器」恋腕。

ExtensionClassLoader 負(fù)責(zé)加載 JVM 擴展類,比如 swing 系列逆瑞、內(nèi)置的 js 引擎荠藤、xml 解析器 等等,這些庫名通常以 javax 開頭获高,它們的 jar 包位于 JAVA_HOME/lib/ext/*.jar 中哈肖,有很多 jar 包。

AppClassLoader 才是直接面向我們用戶的加載器念秧,它會加載 Classpath 環(huán)境變量里定義的路徑中的 jar 包和目錄淤井。我們自己編寫的代碼以及使用的第三方 jar 包通常都是由它來加載的。

那些位于網(wǎng)絡(luò)上靜態(tài)文件服務(wù)器提供的 jar 包和 class文件摊趾,jdk 內(nèi)置了一個 URLClassLoader币狠,用戶只需要傳遞規(guī)范的網(wǎng)絡(luò)路徑給構(gòu)造器,就可以使用 URLClassLoader 來加載遠(yuǎn)程類庫了砾层。URLClassLoader 不但可以加載遠(yuǎn)程類庫漩绵,還可以加載本地路徑的類庫,取決于構(gòu)造器中不同的地址形式肛炮。ExtensionClassLoader 和 AppClassLoader 都是 URLClassLoader 的子類止吐,它們都是從本地文件系統(tǒng)里加載類庫

AppClassLoader 可以由 ClassLoader 類提供的靜態(tài)方法 getSystemClassLoader() 得到铸董,它就是我們所說的「系統(tǒng)類加載器」祟印,我們用戶平時編寫的類代碼通常都是由它加載的肴沫。當(dāng)我們的 main 方法執(zhí)行的時候粟害,這第一個用戶類的加載器就是 AppClassLoader。

ClassLoader 傳遞性

程序在運行過程中颤芬,遇到了一個未知的類悲幅,它會選擇哪個 ClassLoader 來加載它呢套鹅?虛擬機的策略是使用調(diào)用者 Class 對象的 ClassLoader 來加載當(dāng)前未知的類。何為調(diào)用者 Class 對象汰具?就是在遇到這個未知的類時卓鹿,虛擬機肯定正在運行一個方法調(diào)用(靜態(tài)方法或者實例方法),這個方法掛在哪個類上面留荔,那這個類就是調(diào)用者 Class 對象吟孙。前面我們提到每個 Class 對象里面都有一個 classLoader 屬性記錄了當(dāng)前的類是由誰來加載的。

因為 ClassLoader 的傳遞性聚蝶,所有延遲加載的類都會由初始調(diào)用 main 方法的這個 ClassLoader 全全負(fù)責(zé)杰妓,它就是 AppClassLoader。

雙親委派

前面我們提到 AppClassLoader 只負(fù)責(zé)加載 Classpath 下面的類庫碘勉,如果遇到?jīng)]有加載的系統(tǒng)類庫怎么辦巷挥,AppClassLoader 必須將系統(tǒng)類庫的加載工作交給 BootstrapClassLoader 和 ExtensionClassLoader 來做,這就是我們常說的「雙親委派」验靡。

AppClassLoader 在加載一個未知的類名時倍宾,它并不是立即去搜尋 Classpath,它會首先將這個類名稱交給 ExtensionClassLoader 來加載胜嗓,如果 ExtensionClassLoader 可以加載高职,那么 AppClassLoader 就不用麻煩了。否則它就會搜索 Classpath 兼蕊。

而 ExtensionClassLoader 在加載一個未知的類名時初厚,它也并不是立即搜尋 ext 路徑,它會首先將類名稱交給 BootstrapClassLoader 來加載孙技,如果 BootstrapClassLoader 可以加載产禾,那么 ExtensionClassLoader 也就不用麻煩了。否則它就會搜索 ext 路徑下的 jar 包牵啦。

這三個 ClassLoader 之間形成了級聯(lián)的父子關(guān)系亚情,每個 ClassLoader 都很懶,盡量把工作交給父親做哈雏,父親干不了了自己才會干楞件。每個 ClassLoader 對象內(nèi)部都會有一個 parent 屬性指向它的父加載器。

class ClassLoader {
  ...
  private final ClassLoader parent;
  ...
}

值得注意的是圖中的 ExtensionClassLoader 的 parent 指針畫了虛線裳瘪,這是因為它的 parent 的值是 null土浸,當(dāng) parent 字段是 null 時就表示它的父加載器是「根加載器」。如果某個 Class 對象的 classLoader 屬性值是 null彭羹,那么就表示這個類也是「根加載器」加載的黄伊。

Class.forName

當(dāng)我們在使用 jdbc 驅(qū)動時,經(jīng)常會使用 Class.forName 方法來動態(tài)加載驅(qū)動類派殷。

Class.forName("com.mysql.cj.jdbc.Driver");

其原理是 mysql 驅(qū)動的 Driver 類里有一個靜態(tài)代碼塊还最,它會在 Driver 類被加載的時候執(zhí)行墓阀。這個靜態(tài)代碼塊會將 mysql 驅(qū)動實例注冊到全局的 jdbc 驅(qū)動管理器里。

class Driver {
  static {
    try {
       java.sql.DriverManager.registerDriver(new Driver());
    } catch (SQLException E) {
       throw new RuntimeException("Can't register driver!");
    }
  }
  ...
}

forName 方法同樣也是使用調(diào)用者 Class 對象的 ClassLoader 來加載目標(biāo)類拓轻。不過 forName 還提供了多參數(shù)版本斯撮,可以指定使用哪個 ClassLoader 來加載
Class<?> forName(String name, boolean initialize, ClassLoader cl)

通過這種形式的 forName 方法可以突破內(nèi)置加載器的限制,通過使用自定類加載器允許我們自由加載其它任意來源的類庫扶叉。根據(jù) ClassLoader 的傳遞性勿锅,目標(biāo)類庫傳遞引用到的其它類庫也將會使用自定義加載器加載。

自定義加載器

ClassLoader 里面有三個重要的方法 loadClass()枣氧、findClass() 和 defineClass()粱甫。

loadClass() 方法是加載目標(biāo)類的入口,它首先會查找當(dāng)前 ClassLoader 以及它的雙親里面是否已經(jīng)加載了目標(biāo)類作瞄,如果沒有找到就會讓雙親嘗試加載茶宵,如果雙親都加載不了,就會調(diào)用 findClass() 讓自定義加載器自己來加載目標(biāo)類宗挥。ClassLoader 的 findClass() 方法是需要子類來覆蓋的乌庶,不同的加載器將使用不同的邏輯來獲取目標(biāo)類的字節(jié)碼。拿到這個字節(jié)碼之后再調(diào)用 defineClass() 方法將字節(jié)碼轉(zhuǎn)換成 Class 對象契耿。下面我使用偽代碼表示一下基本過程

class ClassLoader {

  // 加載入口瞒大,定義了雙親委派規(guī)則
  Class loadClass(String name) {
    // 是否已經(jīng)加載了
    Class t = this.findFromLoaded(name);
    if(t == null) {
      // 交給雙親
      t = this.parent.loadClass(name)
    }
    if(t == null) {
      // 雙親都不行,只能靠自己了
      t = this.findClass(name);
    }
    return t;
  }

  // 交給子類自己去實現(xiàn)
  Class findClass(String name) {
    throw ClassNotFoundException();
  }

  // 組裝Class對象
  Class defineClass(byte[] code, String name) {
    return buildClassFromCode(code, name);
  }
}

class CustomClassLoader extends ClassLoader {

  Class findClass(String name) {
    // 尋找字節(jié)碼
    byte[] code = findCodeFromSomewhere(name);
    // 組裝Class對象
    return this.defineClass(code, name);
  }
}

自定義類加載器不易破壞雙親委派規(guī)則搪桂,不要輕易覆蓋 loadClass 方法透敌。否則可能會導(dǎo)致自定義加載器無法加載內(nèi)置的核心類庫。在使用自定義加載器時踢械,要明確好它的父加載器是誰酗电,將父加載器通過子類的構(gòu)造器傳入。如果父類加載器是 null内列,那就表示父加載器是「根加載器」撵术。

// ClassLoader 構(gòu)造器
protected ClassLoader(String name, ClassLoader parent);

雙親委派規(guī)則可能會變成三親委派,四親委派话瞧,取決于你使用的父加載器是誰嫩与,它會一直遞歸委派到根加載器。

Class.forName vs ClassLoader.loadClass

這兩個方法都可以用來加載目標(biāo)類交排,它們之間有一個小小的區(qū)別划滋,那就是 Class.forName() 方法可以獲取原生類型的 Class,而 ClassLoader.loadClass() 則會報錯埃篓。

Class<?> x = Class.forName("[I");
System.out.println(x);

x = ClassLoader.getSystemClassLoader().loadClass("[I");
System.out.println(x);

----------------------------------------------------------------------------------------------------------------------------
class [I

Exception in thread "main" java.lang.ClassNotFoundException: [I
...

鉆石依賴

項目管理上有一個著名的概念叫著「鉆石依賴」处坪,是指軟件依賴導(dǎo)致同一個軟件包的兩個版本需要共存而不能沖突。

我們平時使用的 maven 是這樣解決鉆石依賴的,它會從多個沖突的版本中選擇一個來使用稻薇,如果不同的版本之間兼容性很糟糕,那么程序?qū)o法正常編譯運行胶征。Maven 這種形式叫「扁平化」依賴管理塞椎。

使用 ClassLoader 可以解決鉆石依賴問題。不同版本的軟件包使用不同的 ClassLoader 來加載睛低,位于不同 ClassLoader 中名稱一樣的類實際上是不同的類案狠。下面讓我們使用 URLClassLoader 來嘗試一個簡單的例子,它默認(rèn)的父加載器是 AppClassLoader

$ cat ~/source/jcl/v1/Dep.java
public class Dep {
    public void print() {
        System.out.println("v1");
    }
}

$ cat ~/source/jcl/v2/Dep.java
public class Dep {
  public void print() {
    System.out.println("v1");
  }
}

$ cat ~/source/jcl/Test.java
public class Test {
    public static void main(String[] args) throws Exception {
        String v1dir = "file:///Users/qianwp/source/jcl/v1/";
        String v2dir = "file:///Users/qianwp/source/jcl/v2/";
        URLClassLoader v1 = new URLClassLoader(new URL[]{new URL(v1dir)});
        URLClassLoader v2 = new URLClassLoader(new URL[]{new URL(v2dir)});

        Class<?> depv1Class = v1.loadClass("Dep");
        Object depv1 = depv1Class.getConstructor().newInstance();
        depv1Class.getMethod("print").invoke(depv1);

        Class<?> depv2Class = v2.loadClass("Dep");
        Object depv2 = depv2Class.getConstructor().newInstance();
        depv2Class.getMethod("print").invoke(depv2);

        System.out.println(depv1Class.equals(depv2Class));
   }
}

在運行之前钱雷,我們需要對依賴的類庫進行編譯

$ cd ~/source/jcl/v1
$ javac Dep.java
$ cd ~/source/jcl/v2
$ javac Dep.java
$ cd ~/source/jcl
$ javac Test.java
$ java Test
v1
v2
false

在這個例子中如果兩個 URLClassLoader 指向的路徑是一樣的骂铁,下面這個表達式還是 false,因為即使是同樣的字節(jié)碼用不同的 ClassLoader 加載出來的類都不能算同一個類

depv1Class.equals(depv2Class)

我們還可以讓兩個不同版本的 Dep 類實現(xiàn)同一個接口罩抗,這樣可以避免使用反射的方式來調(diào)用 Dep 類里面的方法拉庵。

Class<?> depv1Class = v1.loadClass("Dep");
IPrint depv1 = (IPrint)depv1Class.getConstructor().newInstance();
depv1.print()

ClassLoader 固然可以解決依賴沖突問題,不過它也限制了不同軟件包的操作界面必須使用反射或接口的方式進行動態(tài)調(diào)用套蒂。Maven 沒有這種限制钞支,它依賴于虛擬機的默認(rèn)懶惰加載策略,運行過程中如果沒有顯示使用定制的 ClassLoader操刀,那么從頭到尾都是在使用 AppClassLoader烁挟,而不同版本的同名類必須使用不同的 ClassLoader 加載,所以 Maven 不能完美解決鉆石依賴骨坑。

如果你想知道有沒有開源的包管理工具可以解決鉆石依賴的撼嗓,我推薦你了解一下 sofa-ark,它是螞蟻金服開源的輕量級類隔離框架欢唾。

分工與合作

這里我們重新理解一下 ClassLoader 的意義且警,它相當(dāng)于類的命名空間,起到了類隔離的作用礁遣。位于同一個 ClassLoader 里面的類名是唯一的振湾,不同的 ClassLoader 可以持有同名的類。ClassLoader 是類名稱的容器亡脸,是類的沙箱押搪。



不同的 ClassLoader 之間也會有合作,它們之間的合作是通過 parent 屬性和雙親委派機制來完成的浅碾。parent 具有更高的加載優(yōu)先級大州。除此之外,parent 還表達了一種共享關(guān)系垂谢,當(dāng)多個子 ClassLoader 共享同一個 parent 時厦画,那么這個 parent 里面包含的類可以認(rèn)為是所有子 ClassLoader 共享的。這也是為什么 BootstrapClassLoader 被所有的類加載器視為祖先加載器,JVM 核心類庫自然應(yīng)該被共享根暑。

Thread.contextClassLoader

如果你稍微閱讀過 Thread 的源代碼力试,你會在它的實例字段中發(fā)現(xiàn)有一個字段非常特別

class Thread {
  ...
  private ClassLoader contextClassLoader;

  public ClassLoader getContextClassLoader() {
    return contextClassLoader;
  }

  public void setContextClassLoader(ClassLoader cl) {
    this.contextClassLoader = cl;
  }
  ...
}

contextClassLoader「線程上下文類加載器」,這究竟是什么東西排嫌?

首先 contextClassLoader 是那種需要顯示使用的類加載器畸裳,如果你沒有顯示使用它,也就永遠(yuǎn)不會在任何地方用到它淳地。你可以使用下面這種方式來顯示使用它

Thread.currentThread().getContextClassLoader().loadClass(name);

這意味著如果你使用 forName(string name) 方法加載目標(biāo)類怖糊,它不會自動使用 contextClassLoader。那些因為代碼上的依賴關(guān)系而懶惰加載的類也不會自動使用 contextClassLoader來加載颇象。

其次線程的 contextClassLoader 是從父線程那里繼承過來的伍伤,所謂父線程就是創(chuàng)建了當(dāng)前線程的線程。程序啟動時的 main 線程的 contextClassLoader 就是 AppClassLoader遣钳。這意味著如果沒有人工去設(shè)置扰魂,那么所有的線程的 contextClassLoader 都是 AppClassLoader。

那這個 contextClassLoader 究竟是做什么用的蕴茴?我們要使用前面提到了類加載器分工與合作的原理來解釋它的用途阅爽。

它可以做到跨線程共享類,只要它們共享同一個 contextClassLoader荐开。父子線程之間會自動傳遞 contextClassLoader付翁,所以共享起來將是自動化的。

如果不同的線程使用不同的 contextClassLoader晃听,那么不同的線程使用的類就可以隔離開來百侧。

如果我們對業(yè)務(wù)進行劃分,不同的業(yè)務(wù)使用不同的線程池能扒,線程池內(nèi)部共享同一個 contextClassLoader佣渴,線程池之間使用不同的 contextClassLoader,就可以很好的起到隔離保護的作用初斑,避免類版本沖突辛润。

如果我們不去定制 contextClassLoader,那么所有的線程將會默認(rèn)使用 AppClassLoader见秤,所有的類都將會是共享的砂竖。

線程的 contextClassLoader 使用場合比較罕見,如果上面的邏輯晦澀難懂也不必過于計較鹃答。

JDK9 增加了模塊功能之后對類加載器的結(jié)構(gòu)設(shè)計做了一定程度的修改乎澄,不過類加載器的原理還是類似的,作為類的容器测摔,它起到類隔離的作用置济,同時還需要依靠雙親委派機制來建立不同的類加載器之間的合作關(guān)系解恰。

轉(zhuǎn)載自:https://mp.weixin.qq.com/s/HZEFKZXu_AUr4HqD7M2H0g


留言提問回答區(qū)

1、jvm是在執(zhí)行java helloworld的時候啟動的嗎浙于?
不是的护盈!在你運行程序的時候 JVM 就已啟動,它會找到程序入口main方法羞酗,并加載和執(zhí)行打印 helloworld 腐宋。

2、文中說的每個Class對象都有一個classloader屬性整慎,這個classloader初始化的時候賦的值是appclassloader嗎?
需要分為兩種情況:一個 ClassLoader 創(chuàng)建時如果沒有指定 parent围苫,那么它的 parent 默認(rèn)就是 AppClassLoader 裤园。但當(dāng)這個 ClassLoader 由外部類創(chuàng)建 ClassLoader 時直接指定一個 ClassLoader 為 parent 的話,那么它的類型由父加載器決定剂府。
可通過查看 ClassLoader 源碼獲知答案:


public abstract class ClassLoader {

// The parent class loader for delegation
// Note: VM hardcoded the offset of this field, thus all new fields
// must be added *after* it.
private final ClassLoader parent;
// The class loader for the system
    // @GuardedBy("ClassLoader.class")
private static ClassLoader scl;

private ClassLoader(Void unused, ClassLoader parent) {
    this.parent = parent;
    ...
}
protected ClassLoader(ClassLoader parent) {
    this(checkCreateClassLoader(), parent);
}
protected ClassLoader() {
    this(checkCreateClassLoader(), getSystemClassLoader());
}
public final ClassLoader getParent() {
    if (parent == null)
        return null;
    return parent;
}
public static ClassLoader getSystemClassLoader() {
    initSystemClassLoader();
    if (scl == null) {
        return null;
    }
    return scl;
}

private static synchronized void initSystemClassLoader() {
    if (!sclSet) {
        if (scl != null)
            throw new IllegalStateException("recursive invocation");
        sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
        if (l != null) {
            Throwable oops = null;
            //通過Launcher獲取ClassLoader
            scl = l.getClassLoader();
            try {
                scl = AccessController.doPrivileged(
                    new SystemClassLoaderAction(scl));
            } catch (PrivilegedActionException pae) {
                oops = pae.getCause();
                if (oops instanceof InvocationTargetException) {
                    oops = oops.getCause();
                }
            }
            if (oops != null) {
                if (oops instanceof Error) {
                    throw (Error) oops;
                } else {
                    // wrap the exception
                    throw new Error(oops);
                }
            }
        }
        sclSet = true;
    }
}
}


本篇到此完結(jié)~

期待您點擊關(guān)注或點擊頭像瀏覽更多移動端開發(fā)技術(shù)干貨拧揽!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市腺占,隨后出現(xiàn)的幾起案子淤袜,更是在濱河造成了極大的恐慌,老刑警劉巖衰伯,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铡羡,死亡現(xiàn)場離奇詭異,居然都是意外死亡意鲸,警方通過查閱死者的電腦和手機烦周,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來怎顾,“玉大人读慎,你說我怎么就攤上這事』蔽恚” “怎么了夭委?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長募强。 經(jīng)常有香客問我株灸,道長,這世上最難降的妖魔是什么擎值? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任蚂且,我火速辦了婚禮,結(jié)果婚禮上幅恋,老公的妹妹穿的比我還像新娘杏死。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布淑翼。 她就那樣靜靜地躺著腐巢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪玄括。 梳的紋絲不亂的頭發(fā)上冯丙,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天,我揣著相機與錄音遭京,去河邊找鬼胃惜。 笑死,一個胖子當(dāng)著我的面吹牛哪雕,可吹牛的內(nèi)容都是我干的船殉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼斯嚎,長吁一口氣:“原來是場噩夢啊……” “哼利虫!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起堡僻,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤糠惫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后钉疫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體硼讽,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年牲阁,在試婚紗的時候發(fā)現(xiàn)自己被綠了理郑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡咨油,死狀恐怖您炉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情役电,我是刑警寧澤赚爵,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站法瑟,受9級特大地震影響冀膝,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜霎挟,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一窝剖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧酥夭,春花似錦赐纱、人聲如沸脊奋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽诚隙。三九已至,卻和暖如春起胰,著一層夾襖步出監(jiān)牢的瞬間久又,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工效五, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留地消,地道東北人。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓畏妖,卻偏偏與公主長得像脉执,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子瓜客,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,107評論 2 356