深入理解JVM(3) : 虛擬機(jī)類加載機(jī)制

本文根據(jù)《深入理解java虛擬機(jī)》第7章內(nèi)容整理

一、基本概念


虛擬機(jī)把描述類的數(shù)據(jù)從Class文件加載到內(nèi)存,并對(duì)數(shù)據(jù)進(jìn)行校驗(yàn)、轉(zhuǎn)換解析和初始化,最終形成可以被虛擬機(jī)直接使用的Java類型,這就是虛擬機(jī)的類加載機(jī)制。

與那些在編譯時(shí)需要進(jìn)行鏈接工作的語(yǔ)言不同,在Java語(yǔ)言里,類型的加載、連接和初始化過(guò)程都是在程序運(yùn)行期間完成的,這種策略雖然會(huì)令類加載時(shí)稍微增加一些性能開(kāi)銷,但是會(huì)為Java應(yīng)用程序提供高度的靈活性,Java可以動(dòng)態(tài)擴(kuò)展的語(yǔ)言特性就是依賴運(yùn)行期間動(dòng)態(tài)加載和動(dòng)態(tài)鏈接這個(gè)特點(diǎn)實(shí)現(xiàn)的。

類的生命周期:
類從被加載到虛擬機(jī)內(nèi)存中開(kāi)始,到卸載出內(nèi)存為止,它的整個(gè)生命周期包括:加載、驗(yàn)證、準(zhǔn)備、解析、初始化、使用和卸載7個(gè)階段。其中驗(yàn)證、準(zhǔn)備、解析3個(gè)部分統(tǒng)稱為連接。

類的生命周期

這些階段通常都是互相交叉地混合式進(jìn)行的,通常會(huì)在一個(gè)階段執(zhí)行的過(guò)程中調(diào)用、激活另一個(gè)階段。
例如:加載階段與連接階段的部分內(nèi)容(如一部分字節(jié)碼的文件格式驗(yàn)證動(dòng)作)是交叉進(jìn)行的,加載階段尚未完成,連接階段可能已經(jīng)開(kāi)始,但這些夾在加載階段之中進(jìn)行的動(dòng)作,仍然屬于連接階段的內(nèi)容,這兩個(gè)階段的開(kāi)始時(shí)間仍然保持著固定的先后順序。

二、類加載的時(shí)機(jī)


Java虛擬機(jī)規(guī)范沒(méi)有強(qiáng)制性約束在什么時(shí)候開(kāi)始類加載過(guò)程,但是對(duì)于初始化階段,虛擬機(jī)規(guī)范則嚴(yán)格規(guī)定了有且只有5種情況必需立即對(duì)類進(jìn)行“初始化”(而加載、驗(yàn)證、準(zhǔn)備階段自然需要在此之前開(kāi)始)。

  1. 遇到newgetstatic、putstaticinvokestatic這4條字節(jié)碼指令時(shí),如果類沒(méi)有進(jìn)行過(guò)初始化,則需要先觸發(fā)其初始化。
    生成這4條指令最常見(jiàn)的Java代碼場(chǎng)景是:使用new關(guān)鍵字實(shí)例化對(duì)象時(shí)、讀取或者設(shè)置一個(gè)類的靜態(tài)字段(被final修飾、已在編譯器把結(jié)果放入常量池的靜態(tài)字段除外)時(shí)、以及調(diào)用一個(gè)類的靜態(tài)方法的時(shí)候。

  2. 使用java.lang.reflect包的方法對(duì)類進(jìn)行反射調(diào)用的時(shí)候,如果類沒(méi)有進(jìn)行過(guò)初始化,則需要先觸發(fā)其初始化。

  3. 當(dāng)初始化一個(gè)類的時(shí)候,如果發(fā)現(xiàn)其父類還沒(méi)有進(jìn)行過(guò)初始化,則需要先觸發(fā)其父類的初始化。

  4. 當(dāng)虛擬機(jī)啟動(dòng)時(shí),用戶需要指定一個(gè)要執(zhí)行的主類(包含main()方法的那個(gè)類),虛擬機(jī)會(huì)先初始化這個(gè)類。

  5. 當(dāng)使用JDK1.7的動(dòng)態(tài)語(yǔ)言支持時(shí),如果一個(gè)java.lang.invoke.MethodHandle實(shí)例最后的解析結(jié)果 REF_getStatic、REF_putStaticREF_invokeStatic的方法句柄,并且這個(gè)方法句柄所對(duì)應(yīng)的類沒(méi)有進(jìn)行過(guò)初始化,則需要先觸發(fā)其初始化。

對(duì)于這5種會(huì)觸發(fā)類進(jìn)行初始化的場(chǎng)景,在java虛擬機(jī)規(guī)范中限定了“有且只有”這5種場(chǎng)景會(huì)觸發(fā)。
這5種場(chǎng)景中的行為稱為對(duì)一個(gè)類的主動(dòng)引用,除此以外的所有引用類的方式都不會(huì)觸發(fā)類的初始化,稱為被動(dòng)引用。

被動(dòng)引用示例:

  1. 通過(guò)子類引用父類的靜態(tài)字段,不會(huì)導(dǎo)致子類初始化。
public class SuperClass {  
    static{  
        System.out.println("SuperClass init!");  
    }  
    public static int value = 123;  
}  
public class SubClass extends SuperClass {  
    static{  
        System.out.println("SubClass init!");  
    }  
}  
public class NotInitialization {  
    public static void main(String[] args) {  
        System.out.println(SubClass.value);  
    }  
} 
SuperClass init!  
123  

對(duì)于靜態(tài)字段,只有直接定義這個(gè)字段的類才會(huì)被初始化,因此通過(guò)其子類來(lái)引用父類中定義的靜態(tài)字段,只會(huì)觸發(fā)父類的初始化而不會(huì)觸發(fā)子類的初始化。

  1. 通過(guò)數(shù)組定義來(lái)引用類,不會(huì)觸發(fā)此類的初始化。
public class SuperClass {  
    static{  
        System.out.println("SuperClass init!");  
    }  
    public static int value = 123;  
}  
public class NotInitialization {  
    public static void main(String[] args) {  
        SuperClass[] scs = new SuperClass[10];  
    }  
} 

輸出結(jié)果為空
沒(méi)有輸出SuperClass init!說(shuō)明沒(méi)有觸發(fā)類com.zm.classloading.SuperClass的初始化階段,但是這段代碼會(huì)觸發(fā)[Lcom.zm.classloading.SuperClass類的初始化階段。這個(gè)類是由虛擬機(jī)自動(dòng)生成的,直接繼承于java.lang.Object的子類,創(chuàng)建動(dòng)作由字節(jié)碼指令 newarray觸發(fā)。

  1. 常量在編譯階段會(huì)存入調(diào)用類的常量池中,本質(zhì)上并沒(méi)有直接引用到定義常量的類,因此不會(huì)觸發(fā)定義常量的類的初始化。
public class ConstClass {
    static{
        System.out.println("ConstClass init!");
    }
    public static final String HELLOWORLD = "hello world";
}
public class NotInitialization {
    public static void main(String[] args) {
        System.out.println(ConstClass.HELLOWORLD);
    }
}
hello world

雖然在Java源碼中引用了ConstClass類中的常量HELLOWORLD,但其實(shí)在編譯階段通過(guò)常量傳播優(yōu)化,已經(jīng)將此常量的值hello world存儲(chǔ)到了NotInitialization類的常量池中,以后NotInitialization對(duì)于常量ConstClass.HELLOWORLD的引用實(shí)際上都被轉(zhuǎn)化為NotInitialization類對(duì)自身常量池的引用了。實(shí)際上NotInitialization的Class文件之中已經(jīng)不存在ConstClass類的符號(hào)引用入口了。

接口的加載過(guò)程:

接口也有初始化過(guò)程,這與類是一致的,上述的代碼中都是使用靜態(tài)語(yǔ)句塊static{}來(lái)輸出初始化信息的,而接口中不能使用static{}語(yǔ)句塊,但編譯器仍然會(huì)為接口生成<clinit>()類構(gòu)造器,用于初始化接口中所定義的成員變量。

接口的加載過(guò)程與類加載的區(qū)別在于上面提到的5種“有且僅有”需要初始化場(chǎng)景中的第3種:當(dāng)一個(gè)類在初始化時(shí)要求其父類全部都已經(jīng)初始化過(guò)了,但是一個(gè)接口在初始化時(shí),并不要求其父接口都全部完成了初始化,只有在真正用到父接口的時(shí)候(如引用父接口中定義的常量)才會(huì)初始化。

三、類加載的過(guò)程


下面我們來(lái)詳細(xì)了解類加載的全過(guò)程,也就是加載、驗(yàn)證、準(zhǔn)備、解析和初始化這五個(gè)階段的過(guò)程。

  1. 加載
    首先要說(shuō)明的是“加載”(Loading)階段只是“類加載”(Class Loading)過(guò)程的一個(gè)階段。不要混淆了這兩個(gè)概念。在加載階段,虛擬機(jī)需要完成以下三件事情:

    1)通過(guò)一個(gè)類的全限定名來(lái)獲取定義此類的二進(jìn)制字節(jié)流。
    2)將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)。
    3)在內(nèi)存中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象,作為方法區(qū)這個(gè)類各種數(shù)據(jù)的訪問(wèn)入口。

相對(duì)于類加載過(guò)程的其他階段,一個(gè)非數(shù)組類的加載階段(準(zhǔn)確的說(shuō),是加載階段中獲取類的二進(jìn)制字節(jié)流的動(dòng)作)是開(kāi)發(fā)人員可控性最強(qiáng)的,因?yàn)樵撾A段既可以使用系統(tǒng)提供的引導(dǎo)類加載器完成,也可以由用戶自定義的類加載器來(lái)完成,開(kāi)發(fā)人員可以通過(guò)定義自己的類加載器去控制字節(jié)流的獲取方式。

對(duì)于數(shù)組類而言,數(shù)組類本身不通過(guò)類加載器創(chuàng)建,它是由虛擬機(jī)直接創(chuàng)建的。但是數(shù)組類的元數(shù)據(jù)類型最終還是要靠類加載器去創(chuàng)建的。

加載階段完成后,虛擬機(jī)外部的二進(jìn)制字節(jié)流就按照虛擬機(jī)所需的格式存儲(chǔ)在方法區(qū)之中,方法區(qū)中的數(shù)據(jù)存儲(chǔ)格式由JVM自行定義。然后在內(nèi)存中實(shí)例化一個(gè)java.lang.Class對(duì)象,這個(gè)對(duì)象將作為程序訪問(wèn)方法區(qū)中這些類型數(shù)據(jù)的外部接口。(對(duì)于HotSpot虛擬機(jī)而言,Class對(duì)象比較特殊,它雖然是對(duì)象,但是存放在方法區(qū)里面)

  1. 驗(yàn)證
    驗(yàn)證是連接階段的第一步,這一階段的目的是為了確保Class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)自身的安全。
    整體上看,驗(yàn)證階段會(huì)完成下面4個(gè)階段的檢驗(yàn)動(dòng)作:文件格式驗(yàn)證、元數(shù)據(jù)驗(yàn)證、字節(jié)碼驗(yàn)證和符號(hào)引用驗(yàn)證。
  • 1)文件格式驗(yàn)證:這一階段要驗(yàn)證字節(jié)流是否符合Class文件格式的規(guī)范,并且能被當(dāng)前版本的虛擬機(jī)處理。
  • 是否以魔術(shù)0xCAFEBABE開(kāi)頭;
  • 主次版本號(hào)是否是在當(dāng)前虛擬機(jī)處理范圍之內(nèi);
  • 常量池的常量中是否有不被支持的常量類型(檢查常量tag標(biāo)志);
    . . .
第一階段的主要目的是保證輸入的字節(jié)流能正確地解析并存儲(chǔ)于方法區(qū)之內(nèi),格式上符合一個(gè)Java類型信息的要求。

這階段的驗(yàn)證是基于二進(jìn)制字節(jié)流進(jìn)行的,只有通過(guò)了這個(gè)階段的驗(yàn)證后,字節(jié)流才會(huì)進(jìn)入內(nèi)存的方法區(qū)中進(jìn)行存儲(chǔ),所以后面的3個(gè)驗(yàn)證階段全部是基于方法區(qū)的存儲(chǔ)結(jié)構(gòu)進(jìn)行的,不會(huì)再直接操作字節(jié)流。

  • 2)元數(shù)據(jù)驗(yàn)證:這一階段主要是對(duì)字節(jié)碼描述的信息進(jìn)行語(yǔ)義分析,以保證其描述的信息符合Java語(yǔ)言規(guī)范的要求。
  • 這個(gè)類是否有父類;
  • 這個(gè)類的父類是否繼承了不允許被繼承的類(被final修飾的類);
  • 如果這個(gè)類不是抽象類,是否實(shí)現(xiàn)了其父類或接口之中要求實(shí)現(xiàn)的所有方法;
  • 類中的字段、方法是否與父類產(chǎn)生矛盾(如覆蓋了父類的final字段,不符合規(guī)則的重載);
    . . .
第二階段的主要目的是對(duì)類的元數(shù)據(jù)信息進(jìn)行語(yǔ)義校驗(yàn),保證不存在不符合Java規(guī)范的元數(shù)據(jù)類型。
  • 3)字節(jié)碼驗(yàn)證:這一階段是整個(gè)驗(yàn)證過(guò)程中最復(fù)雜的一個(gè)階段,主要目的是通過(guò)數(shù)據(jù)流和控制流分析,確定程序語(yǔ)義是合法的、符合邏輯的。
    在第二階段對(duì)元數(shù)據(jù)信息中的數(shù)據(jù)類型做完校驗(yàn)后,這階段將對(duì)類的方法體進(jìn)行校驗(yàn)分析。保證被校驗(yàn)類的方法在運(yùn)行時(shí)不會(huì)做出危害虛擬機(jī)安全的事件。

  • 保證跳轉(zhuǎn)指令不會(huì)跳轉(zhuǎn)到方法體之外的字節(jié)碼指令上;

  • 保證方法體中的類型轉(zhuǎn)換是有效的,例如將子類對(duì)象賦給父類對(duì)象是安全的,但是把父類對(duì)象賦值給子類數(shù)據(jù)類型,甚至是和它毫無(wú)繼承關(guān)系的一個(gè)數(shù)據(jù)類型,則是危險(xiǎn)和不安全的;

  • 保證任意時(shí)刻操作數(shù)棧的數(shù)據(jù)類型與指令代碼序列都能配合工作,例如不會(huì)出現(xiàn)在操作數(shù)棧中放置了一個(gè)int類型數(shù)據(jù),使用時(shí)卻按long類型來(lái)加載人本地變量表中。
    . . .

  • 4)符號(hào)引用驗(yàn)證:這一階段主要是在虛擬機(jī)將符號(hào)引用轉(zhuǎn)化為直接引用的時(shí)候進(jìn)行校驗(yàn),這個(gè)轉(zhuǎn)化動(dòng)作是發(fā)生在解析階段。符號(hào)引用可以看做是對(duì)類自身以外(常量池的各種符號(hào)引用)的信息進(jìn)行匹配性的校驗(yàn)。

  • 符號(hào)引用中通過(guò)字符串描述的全限定名是否能找到相應(yīng)的類;

  • 在指定類中是否存在符合方法的字段描述符以及簡(jiǎn)單名稱所描述方法和字段;

  • 符號(hào)引用中的類、字段、方法的訪問(wèn)性(private、public、protected、default)是否可以被當(dāng)前類訪問(wèn);
    . . .

符號(hào)引用驗(yàn)證的目的是確保解析動(dòng)作能正常執(zhí)行,如果無(wú)法通過(guò)符號(hào)引用驗(yàn)證,那么將會(huì)拋出異常。

驗(yàn)證階段對(duì)于虛擬機(jī)的類加載機(jī)制來(lái)說(shuō),是一個(gè)非常重要但不一定是必要的階段。如果所運(yùn)行的全部代碼都已經(jīng)被反復(fù)使用和驗(yàn)證過(guò),在實(shí)施階段就可以考慮使用-Xverify:none參數(shù)來(lái)關(guān)閉大部分的類驗(yàn)證措施,從而縮短虛擬機(jī)類加載的時(shí)間。

  1. 準(zhǔn)備
    準(zhǔn)備階段是正式為類變量分配內(nèi)存并設(shè)置類變量初始值的階段,這些內(nèi)存都將在方法區(qū)中進(jìn)行分配。
  • 這個(gè)時(shí)候進(jìn)行內(nèi)存分配的僅包括類變量(被static修飾的變量),而不包括實(shí)例變量,實(shí)例變量將會(huì)在對(duì)象實(shí)例化時(shí)隨著對(duì)象一起被分配在Java堆中。
  • 這里所說(shuō)的初始值“通常情況”下是數(shù)據(jù)類型的零值,例如public static int value = 123 ;value在準(zhǔn)備階段后的初始值是0而不是123,因?yàn)榇藭r(shí)尚未執(zhí)行任何的Java方法,而把value賦值為123的putStatic指令是程序被編譯后,存放在類構(gòu)造器<clinit>()方法之中,把value賦值為123的動(dòng)作將在初始化階段才會(huì)執(zhí)行。
  • 通常情況下初始值為零值,相對(duì)的會(huì)存在特殊情況:如果類字段的字段屬性表中存在ConstantValue屬性,那在準(zhǔn)備階段變量就會(huì)被初始化為ConstantValue屬性所指定的值,例如public static final int value = 123 編譯時(shí)javac將會(huì)為value生成ConstantValue屬性,在準(zhǔn)備階段虛擬機(jī)就會(huì)根據(jù)ConstantValue的設(shè)置將變量賦值為123。
  1. 解析
    解析階段是虛擬機(jī)將常量池內(nèi)的符號(hào)引用替換為直接引用的過(guò)程。
    在Class文件中符號(hào)引用以CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等類型的常量出現(xiàn)。
  • 符號(hào)引用(Symbolic Reference):
    符號(hào)引用以一組符號(hào)來(lái)描述所引用的目標(biāo),符號(hào)引用可以是任何形式的字面量,只要使用時(shí)能無(wú)歧義的定位到目標(biāo)即可。符號(hào)引用與虛擬機(jī)實(shí)現(xiàn)的內(nèi)存布局無(wú)關(guān),引用的目標(biāo)并不一定已經(jīng)加載在內(nèi)存中。各種虛擬機(jī)實(shí)現(xiàn)的內(nèi)存布局可以各不相同,但是他們能接受的符號(hào)引用必須都是一致的,因?yàn)榉?hào)引用的字面量形式明確定義在Java虛擬機(jī)規(guī)范的Class文件格式中。
  • 直接引用(Direct Reference):
    直接引用可以是直接指向目標(biāo)的指針、相對(duì)偏移量或是一個(gè)能間接定位到目標(biāo)的句柄。直接引用是與虛擬機(jī)實(shí)現(xiàn)的內(nèi)存布局相關(guān)的,同一個(gè)符號(hào)引用在不同的虛擬機(jī)實(shí)例上翻譯出來(lái)的直接引用一般都不相同,如果有了直接引用,那引用的目標(biāo)必定已經(jīng)在內(nèi)存中存在。

對(duì)于同一個(gè)符號(hào)引用可能會(huì)出現(xiàn)多次解析請(qǐng)求,虛擬機(jī)可能會(huì)對(duì)第一次解析的結(jié)果進(jìn)行緩存。
解析動(dòng)作主要針對(duì):類或接口、字段、類方法、接口方法、方法類型、方法句柄和調(diào)用點(diǎn)限定符7類符號(hào)引用進(jìn)行。

個(gè)人理解:一個(gè)java類將會(huì)編譯成一個(gè)class文件。在編譯時(shí),java類并不知道引用類的實(shí)際內(nèi)存地址,因此只能使用符號(hào)引用來(lái)代替。比如org.simple.People類引用org.simple.Tool類,在編譯時(shí)People類并不知道Tool類的實(shí)際內(nèi)存地址,因此只能使用符號(hào)org.simple.Tool(假設(shè))來(lái)表示Tool類的地址。而在類加載器加載People類時(shí),此時(shí)可以通過(guò)虛擬機(jī)獲取Tool類的實(shí)際內(nèi)存地址,因此便可以既將符號(hào)org.simple.Tool替換為Tool類的實(shí)際內(nèi)存地址,及直接引用地址。

  1. 初始化
    類初始化階段是類加載過(guò)程的最后一步,前面的類加載過(guò)程中,除了加載階段用戶應(yīng)用程序可以通過(guò)自定義類加載器參與之外,其余動(dòng)作完全由虛擬機(jī)主導(dǎo)和控制。到了初始化階段,才真正開(kāi)始執(zhí)行類中定義的Java程序代碼。

    初始化階段是執(zhí)行類構(gòu)造器<clinit>()方法的過(guò)程。對(duì)于<clinit>()方法具體介紹如下:

  • (1)<clinit>()方法是由編譯器自動(dòng)收集類中的所有類變量的賦值動(dòng)作和靜態(tài)語(yǔ)句塊(static{}塊)中的語(yǔ)句合并產(chǎn)生的,編譯器收集的順序由語(yǔ)句在源文件中出現(xiàn)的順序所決定。
    靜態(tài)語(yǔ)句塊中只能訪問(wèn)到定義在靜態(tài)語(yǔ)句塊之前的變量,定義在它之后的變量,在前面的靜態(tài)語(yǔ)句塊可以賦值,但是不能訪問(wèn)。
public class Test {
     static{
         i =0;          //給變量賦值可以正常編譯通過(guò)
//  System.out.println(i);  //這句編譯器會(huì)提示“非法向前引用”
     }
     static int i = 1;
}
  • (2)<clinit>()方法與類的構(gòu)造函數(shù)不同,它不需要顯式地調(diào)用父類構(gòu)造器,虛擬機(jī)會(huì)保證在子類的<clinit>()方法執(zhí)行之前,父類的<clinit>()方法已經(jīng)執(zhí)行完畢,因此在虛擬機(jī)中第一個(gè)執(zhí)行的<clinit>()方法的類一定是java.lang.Object

  • (3) 由于父類的<clinit>()方法先執(zhí)行,也就意味著父類中定義的靜態(tài)語(yǔ)句塊要優(yōu)先于子類的變量賦值操作。如下面的例子所示,輸出結(jié)果為2而不是1。

public class Parent {  
    public static int A = 1;  
    static{  
        A = 2;  
    }  
}    
public class Sub extends Parent{  
    public static int B = A;  
}   
public class Test {  
    public static void main(String[] args) {  
        System.out.println(Sub.B);  
    }  
}  
  • (4) <clinit>()方法對(duì)于類或者接口來(lái)說(shuō)并不是必需的,如果一個(gè)類中沒(méi)有靜態(tài)語(yǔ)句塊也沒(méi)有對(duì)變量的賦值操作,那么編譯器可以不為這個(gè)類生成<clinit>()方法。

  • (5) 接口中不能使用靜態(tài)語(yǔ)句塊,但仍然有變量賦值的初始化操作,因此接口也會(huì)生成<clinit>()方法。但是接口與類不同,執(zhí)行接口的<clinit>()方法不需要先執(zhí)行父接口的<clinit>()方法。只有當(dāng)父接口中定義的變量被使用時(shí),父接口才會(huì)被初始化。另外,接口的實(shí)現(xiàn)類在初始化時(shí)也不會(huì)執(zhí)行接口的<clinit>()方法。

  • (6) 虛擬機(jī)會(huì)保證一個(gè)類的<clinit>()方法在多線程環(huán)境中被正確地加鎖和同步。如果有多個(gè)線程去同時(shí)初始化一個(gè)類,那么只會(huì)有一個(gè)線程去執(zhí)行這個(gè)類的<clinit>()方法,其它線程都需要阻塞等待,直到活動(dòng)線程執(zhí)行<clinit>()方法完畢。如果在一個(gè)類的<clinit>()方法中有耗時(shí)很長(zhǎng)的操作,那么就可能造成多個(gè)進(jìn)程阻塞。


虛擬機(jī)把描述類的數(shù)據(jù)從Class文件加載到內(nèi)存,并對(duì)數(shù)據(jù)進(jìn)行校驗(yàn)、轉(zhuǎn)換解析和初始化,最終形成可以被虛擬機(jī)直接使用的Java類型,這就是虛擬機(jī)的類加載機(jī)制。


[2015.08.31]

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

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