App啟動(dòng)時(shí)間優(yōu)化

這是一篇 WWDC 2016 Session 406 的學(xué)習(xí)筆記泻帮,從原理到實(shí)踐講述了如何優(yōu)化 App 的啟動(dòng)時(shí)間玉罐。

App 運(yùn)行理論

main()執(zhí)行前發(fā)生的事

Mach-O 格式

虛擬內(nèi)存基礎(chǔ)

Mach-O 二進(jìn)制的加載

理論速成

Mach-O 術(shù)語

Mach-O 是針對(duì)不同運(yùn)行時(shí)可執(zhí)行文件的文件類型举塔。

文件類型:

Executable: 應(yīng)用的主要二進(jìn)制

Dylib: 動(dòng)態(tài)鏈接庫(又稱 DSO 或 DLL)

Bundle: 不能被鏈接的 Dylib,只能在運(yùn)行時(shí)使用dlopen()加載,可當(dāng)做 macOS 的插件。

Image: executable名挥,dylib 或 bundle

Framework: 包含 Dylib 以及資源文件和頭文件的文件夾

Mach-O 鏡像文件

Mach-O 被劃分成一些 segement,每個(gè) segement 又被劃分成一些 section主守。

segment 的名字都是大寫的禀倔,且空間大小為頁的整數(shù)。頁的大小跟硬件有關(guān)参淫,在 arm64 架構(gòu)一頁是 16KB救湖,其余為 4KB。

section 雖然沒有整數(shù)倍頁大小的限制涎才,但是 section 之間不會(huì)有重疊鞋既。

幾乎所有 Mach-O 都包含這三個(gè)段(segment):

__TEXT,__DATA和__LINKEDIT.

__TEXT包含 Mach header,被執(zhí)行的代碼和只讀常量(如C 字符串)耍铜。只讀可執(zhí)行(r-x)邑闺。

__DATA包含全局變量,靜態(tài)變量等棕兼《妇耍可讀寫(rw-)。

__LINKEDIT包含了加載程序的『元數(shù)據(jù)』程储,比如函數(shù)的名稱和地址。只讀(r–)臂寝。

Mach-O Universal 文件

FAT 二進(jìn)制文件章鲤,將多種架構(gòu)的 Mach-O 文件合并而成。它通過 Fat Header 來記錄不同架構(gòu)在文件中的偏移量咆贬,F(xiàn)at Header 占一頁的空間败徊。

按分頁來存儲(chǔ)這些 segement 和 header 會(huì)浪費(fèi)空間,但這有利于虛擬內(nèi)存的實(shí)現(xiàn)掏缎。

虛擬內(nèi)存

虛擬內(nèi)存就是一層間接尋址(indirection)皱蹦。軟件工程中有句格言就是任何問題都能通過添加一個(gè)間接層來解決。虛擬內(nèi)存解決的是管理所有進(jìn)程使用物理 RAM 的問題眷蜈。通過添加間接層來讓每個(gè)進(jìn)程使用邏輯地址空間沪哺,它可以映射到 RAM 上的某個(gè)物理頁上。這種映射不是一對(duì)一的酌儒,邏輯地址可能映射不到 RAM 上辜妓,也可能有多個(gè)邏輯地址映射到同一個(gè)物理 RAM 上。針對(duì)第一種情況,當(dāng)進(jìn)程要存儲(chǔ)邏輯地址內(nèi)容時(shí)會(huì)觸發(fā) page fault籍滴;第二種情況就是多進(jìn)程共享內(nèi)存酪夷。

對(duì)于文件可以不用一次性讀入整個(gè)文件,可以使用分頁映射(mmap())的方式讀取孽惰。也就是把文件某個(gè)片段映射到進(jìn)程邏輯內(nèi)存的某個(gè)頁上晚岭。當(dāng)某個(gè)想要讀取的頁沒有在內(nèi)存中,就會(huì)觸發(fā) page fault勋功,內(nèi)核只會(huì)讀入那一頁坦报,實(shí)現(xiàn)文件的懶加載。

也就是說 Mach-O 文件中的__TEXT段可以映射到多個(gè)進(jìn)程酝润,并可以懶加載燎竖,且進(jìn)程之間共享內(nèi)存。__DATA段是可讀寫的要销。這里使用到了 Copy-On-Write 技術(shù)构回,簡稱 COW。也就是多個(gè)進(jìn)程共享一頁內(nèi)存空間時(shí)疏咐,一旦有進(jìn)程要做寫操作纤掸,它會(huì)先將這頁內(nèi)存內(nèi)容復(fù)制一份出來,然后重新映射邏輯地址到新的 RAM 頁上浑塞。也就是這個(gè)進(jìn)程自己擁有了那頁內(nèi)存的拷貝借跪。這就涉及到了 clean/dirty page 的概念。dirty page 含有進(jìn)程自己的信息酌壕,而 clean page 可以被內(nèi)核重新生成(重新讀磁盤)掏愁。所以 dirty page 的代價(jià)大于 clean page。

Mach-O 鏡像 加載

所以在多個(gè)進(jìn)程加載 Mach-O 鏡像時(shí)__TEXT和__LINKEDIT因?yàn)橹蛔x卵牍,都是可以共享內(nèi)存的果港。而__DATA因?yàn)榭勺x寫,就會(huì)產(chǎn)生 dirty page糊昙。當(dāng) dyld 執(zhí)行結(jié)束后辛掠,__LINKEDIT就沒用了,對(duì)應(yīng)的內(nèi)存頁會(huì)被回收释牺。

安全

ASLR(Address Space Layout Randomization):地址空間布局隨機(jī)化萝衩,鏡像會(huì)在隨機(jī)的地址上加載。這其實(shí)是一二十年前的舊技術(shù)了没咙。

代碼簽名:可能我們認(rèn)為 Xcode 會(huì)把整個(gè)文件都做加密 hash 并用做數(shù)字簽名猩谊。其實(shí)為了在運(yùn)行時(shí)驗(yàn)證 Mach-O 文件的簽名,并不是每次重復(fù)讀入整個(gè)文件祭刚,而是把每頁內(nèi)容都生成一個(gè)單獨(dú)的加密散列值预柒,并存儲(chǔ)在__LINKEDIT中队塘。這使得文件每頁的內(nèi)容都能及時(shí)被校驗(yàn)確并保不被篡改。

從exec()到main()

exec()是一個(gè)系統(tǒng)調(diào)用宜鸯。系統(tǒng)內(nèi)核把應(yīng)用映射到新的地址空間憔古,且每次起始位置都是隨機(jī)的(因?yàn)槭褂?ASLR)。并將起始位置到0x000000這段范圍的進(jìn)程權(quán)限都標(biāo)記為不可讀寫不可執(zhí)行淋袖。如果是 32 位進(jìn)程鸿市,這個(gè)范圍至少是 4KB;對(duì)于 64 位進(jìn)程則至少是 4GB即碗。NULL 指針引用和指針截?cái)嗾`差都是會(huì)被它捕獲焰情。

dyld加載 dylib 文件

Unix 的前二十年很安逸,因?yàn)槟菚r(shí)還沒有發(fā)明動(dòng)態(tài)鏈接庫剥懒。有了動(dòng)態(tài)鏈接庫后内舟,一個(gè)用于加載鏈接庫的幫助程序被創(chuàng)建。在蘋果的平臺(tái)里是dyld初橘,其他 Unix 系統(tǒng)也有ld.so验游。 當(dāng)內(nèi)核完成映射進(jìn)程的工作后會(huì)將名字為dyld的Mach-O 文件映射到進(jìn)程中的隨機(jī)地址,它將 PC 寄存器設(shè)為dyld的地址并運(yùn)行保檐。dyld在應(yīng)用進(jìn)程中運(yùn)行的工作是加載應(yīng)用依賴的所有動(dòng)態(tài)鏈接庫耕蝉,準(zhǔn)備好運(yùn)行所需的一切,它擁有的權(quán)限跟應(yīng)用一樣夜只。

下面的步驟構(gòu)成了dyld的時(shí)間線:

Load dylibs -> Rebase -> Bind -> ObjC -> Initializers

加載 Dylib

從主執(zhí)行文件的 header 獲取到需要加載的所依賴動(dòng)態(tài)庫列表垒在,而 header 早就被內(nèi)核映射過。然后它需要找到每個(gè) dylib扔亥,然后打開文件讀取文件起始位置场躯,確保它是 Mach-O 文件。接著會(huì)找到代碼簽名并將其注冊(cè)到內(nèi)核旅挤。然后在 dylib 文件的每個(gè) segment 上調(diào)用mmap()踢关。應(yīng)用所依賴的 dylib 文件可能會(huì)再依賴其他 dylib,所以dyld所需要加載的是動(dòng)態(tài)庫列表一個(gè)遞歸依賴的集合谦铃。一般應(yīng)用會(huì)加載 100 到 400 個(gè) dylib 文件耘成,但大部分都是系統(tǒng) dylib榔昔,它們會(huì)被預(yù)先計(jì)算和緩存起來驹闰,加載速度很快。

Fix-ups

在加載所有的動(dòng)態(tài)鏈接庫之后撒会,它們只是處在相互獨(dú)立的狀態(tài)嘹朗,需要將它們綁定起來,這就是 Fix-ups诵肛。代碼簽名使得我們不能修改指令屹培,那樣就不能讓一個(gè) dylib 的調(diào)用另一個(gè) dylib默穴。這時(shí)需要加很多間接層。

現(xiàn)代 code-gen 被叫做動(dòng)態(tài) PIC(Position Independent Code)褪秀,意味著代碼可以被加載到間接的地址上蓄诽。當(dāng)調(diào)用發(fā)生時(shí),code-gen 實(shí)際上會(huì)在__DATA段中創(chuàng)建一個(gè)指向被調(diào)用者的指針媒吗,然后加載指針并跳轉(zhuǎn)過去仑氛。

所以dyld做的事情就是修正(fix-up)指針和數(shù)據(jù)。Fix-up 有兩種類型闸英,rebasing 和 binding锯岖。

Rebasing 和 Binding

Rebasing:在鏡像內(nèi)部調(diào)整指針的指向

Binding:將指針指向鏡像外部的內(nèi)容

可以通過命令行查看 rebase 和 bind 等信息:

xcrun dyldinfo -rebase -bind -lazy_bind myapp.app/myapp

通過這個(gè)命令可以查看所有的 Fix-up。rebase甫何,bind出吹,weak_bind,lazy_bind 都存儲(chǔ)在__LINKEDIT段中辙喂,并可通過LC_DYLD_INFO_ONLY查看各種信息的偏移量和大小捶牢。

建議用 MachOView 查看更加方便直觀。

從dyld源碼層面簡要介紹下 Rebasing 和 Binding 的流程加派。

ImageLoader是一個(gè)用于加載可執(zhí)行文件的基類叫确,它負(fù)責(zé)鏈接鏡像,但不關(guān)心具體文件格式芍锦,因?yàn)檫@些都交給子類去實(shí)現(xiàn)竹勉。每個(gè)可執(zhí)行文件都會(huì)對(duì)應(yīng)一個(gè)ImageLoader實(shí)例。ImageLoaderMachO是用于加載 Mach-O 格式文件的ImageLoader子類娄琉,而ImageLoaderMachOClassic和ImageLoaderMachOCompressed都繼承于ImageLoaderMachO次乓,分別用于加載那些__LINKEDIT段為傳統(tǒng)格式和壓縮格式的 Mach-O 文件。

因?yàn)?dylib 之間有依賴關(guān)系孽水,所以ImageLoader中的好多操作都是沿著依賴鏈遞歸操作的票腰,Rebasing 和 Binding 也不例外,分別對(duì)應(yīng)著recursiveRebase()和recursiveBind()這兩個(gè)方法女气。因?yàn)槭沁f歸杏慰,所以會(huì)自底向上地分別調(diào)用doRebase()和doBind()方法,這樣被依賴的 dylib 總是先于依賴它的 dylib 執(zhí)行 Rebasing 和 Binding炼鞠。傳入doRebase()和doBind()的參數(shù)包含一個(gè)LinkContext上下文缘滥,存儲(chǔ)了可執(zhí)行文件的一堆狀態(tài)和相關(guān)的函數(shù)。

在 Rebasing 和 Binding 前會(huì)判斷是否已經(jīng) Prebinding谒主。如果已經(jīng)進(jìn)行過預(yù)綁定(Prebinding)朝扼,那就不需要 Rebasing 和 Binding 這些 Fix-up 流程了,因?yàn)橐呀?jīng)在預(yù)先綁定的地址加載好了霎肯。

ImageLoaderMachO實(shí)例不使用預(yù)綁定會(huì)有四個(gè)原因:

Mach-O Header 中MH_PREBOUND標(biāo)志位為0

鏡像加載地址有偏移(這個(gè)后面會(huì)講到)

依賴的庫有變化

鏡像使用 flat-namespace擎颖,預(yù)綁定的一部分會(huì)被忽略

LinkContext的環(huán)境變量禁止了預(yù)綁定

ImageLoaderMachO中doRebase()做的事情大致如下:

如果使用預(yù)綁定榛斯,fgImagesWithUsedPrebinding計(jì)數(shù)加一,并return;否則進(jìn)入第二步

如果MH_PREBOUND標(biāo)志位為1(也就是可以預(yù)綁定但沒使用)搂捧,且鏡像在共享內(nèi)存中驮俗,重置上下文中所有的 lazy pointer。(如果鏡像在共享內(nèi)存中允跑,稍后會(huì)在 Binding 過程中綁定意述,所以無需重置)

如果鏡像加載地址偏移量為0,則無需 Rebasing吮蛹,直接return荤崇;否則進(jìn)入第四步

調(diào)用rebase()方法,這才是真正做 Rebasing 工作的方法潮针。如果開啟TEXT_RELOC_SUPPORT宏术荤,會(huì)允許rebase()方法對(duì)__TEXT段做寫操作來對(duì)其進(jìn)行 Fix-up。所以其實(shí)__TEXT只讀屬性并不是絕對(duì)的每篷。

ImageLoaderMachOClassic和ImageLoaderMachOCompressed分別實(shí)現(xiàn)了自己的doRebase()方法瓣戚。實(shí)現(xiàn)邏輯大同小異,同樣會(huì)判斷是否使用預(yù)綁定焦读,并在真正的 Binding 工作時(shí)判斷TEXT_RELOC_SUPPORT宏來決定是否對(duì)__TEXT段做寫操作子库。最后都會(huì)調(diào)用setupLazyPointerHandler在鏡像中設(shè)置dyld的 entry point,放在最后調(diào)用是為了讓主可執(zhí)行文件設(shè)置好__dyld或__program_vars矗晃。

Rebasing

在過去仑嗅,會(huì)把 dylib 加載到指定地址,所有指針和數(shù)據(jù)對(duì)于代碼來說都是對(duì)的张症,dyld就無需做任何 fix-up 了仓技。如今用了 ASLR 后悔將 dylib 加載到新的隨機(jī)地址(actual_address),這個(gè)隨機(jī)的地址跟代碼和數(shù)據(jù)指向的舊地址(preferred_address)會(huì)有偏差俗他,dyld需要修正這個(gè)偏差(slide)脖捻,做法就是將 dylib 內(nèi)部的指針地址都加上這個(gè)偏移量,偏移量的計(jì)算方法如下:

Slide = actual_address - preferred_address

然后就是重復(fù)不斷地對(duì)__DATA段中需要 rebase 的指針加上這個(gè)偏移量兆衅。這就又涉及到 page fault 和 COW地沮。這可能會(huì)產(chǎn)生 I/O 瓶頸,但因?yàn)?rebase 的順序是按地址排列的羡亩,所以從內(nèi)核的角度來看這是個(gè)有次序的任務(wù)摩疑,它會(huì)預(yù)先讀入數(shù)據(jù),減少 I/O 消耗夕春。

Binding

Binding 是處理那些指向 dylib 外部的指針未荒,它們實(shí)際上被符號(hào)(symbol)名稱綁定专挪,也就是個(gè)字符串及志。之前提到__LINKEDIT段中也存儲(chǔ)了需要 bind 的指針片排,以及指針需要指向的符號(hào)。dyld需要找到 symbol 對(duì)應(yīng)的實(shí)現(xiàn)速侈,這需要很多計(jì)算率寡,去符號(hào)表里查找。找到后會(huì)將內(nèi)容存儲(chǔ)到__DATA段中的那個(gè)指針中倚搬。Binding 看起來計(jì)算量比 Rebasing 更大冶共,但其實(shí)需要的 I/O 操作很少,因?yàn)橹?Rebasing 已經(jīng)替 Binding 做過了每界。

ObjC Runtime

Objective-C 中有很多數(shù)據(jù)結(jié)構(gòu)都是靠 Rebasing 和 Binding 來修正(fix-up)的捅僵,比如Class中指向超類的指針和指向方法的指針。

ObjC 是個(gè)動(dòng)態(tài)語言眨层,可以用類的名字來實(shí)例化一個(gè)類的對(duì)象庙楚。這意味著 ObjC Runtime 需要維護(hù)一張映射類名與類的全局表。當(dāng)加載一個(gè) dylib 時(shí)趴樱,其定義的所有的類都需要被注冊(cè)到這個(gè)全局表中馒闷。

C++ 中有個(gè)問題叫做易碎的基類(fragile base class)。ObjC 就沒有這個(gè)問題叁征,因?yàn)闀?huì)在加載時(shí)通過 fix-up 動(dòng)態(tài)類中改變實(shí)例變量的偏移量纳账。

在 ObjC 中可以通過定義類別(Category)的方式改變一個(gè)類的方法。有時(shí)你想要添加方法的類在另一個(gè) dylib 中捺疼,而不在你的鏡像中(也就是對(duì)系統(tǒng)或別人的類動(dòng)刀)疏虫,這時(shí)也需要做些 fix-up。

ObjC 中的 selector 必須是唯一的啤呼。

Initializers

C++ 會(huì)為靜態(tài)創(chuàng)建的對(duì)象生成初始化器议薪。而在 ObjC 中有個(gè)叫+load的方法,然而它被廢棄了媳友,現(xiàn)在建議使用+initialize斯议。對(duì)比詳見:http://stackoverflow.com/questions/13326435/nsobject-load-and-initialize-what-do-they-do

現(xiàn)在有了主執(zhí)行文件,一堆 dylib醇锚,其依賴關(guān)系構(gòu)成了一張巨大的有向圖哼御,那么執(zhí)行初始化器的順序是什么?自頂向上焊唬!按照依賴關(guān)系恋昼,先加載葉子節(jié)點(diǎn),然后逐步向上加載中間節(jié)點(diǎn)赶促,直至最后加載根節(jié)點(diǎn)液肌。這種加載順序確保了安全性,加載某個(gè) dylib 前鸥滨,其所依賴的其余 dylib 文件肯定已經(jīng)被預(yù)先加載嗦哆。

最后dyld會(huì)調(diào)用main()函數(shù)谤祖。main()會(huì)調(diào)用UIApplicationMain()。

改善啟動(dòng)時(shí)間

從點(diǎn)擊 App 圖標(biāo)到加載 App 閃屏之間會(huì)有個(gè)動(dòng)畫老速,我們希望 App 啟動(dòng)速度比這個(gè)動(dòng)畫更快粥喜。雖然不同設(shè)備上 App 啟動(dòng)速度不一樣,但啟動(dòng)時(shí)間最好控制在 400ms橘券。需要注意的是啟動(dòng)時(shí)間一旦超過 20s额湘,系統(tǒng)會(huì)認(rèn)為發(fā)生了死循環(huán)并殺掉 App 進(jìn)程。當(dāng)然啟動(dòng)時(shí)間最好以 App 所支持的最低配置設(shè)備為準(zhǔn)旁舰。直到applicationWillFinishLaunching被調(diào)動(dòng)锋华,App 才啟動(dòng)結(jié)束。

測量啟動(dòng)時(shí)間

Warm launch: App 和數(shù)據(jù)已經(jīng)在內(nèi)存中

Cold launch: App 不在內(nèi)核緩沖存儲(chǔ)器中

冷啟動(dòng)(Cold launch)耗時(shí)才是我們需要測量的重要數(shù)據(jù)箭窜,為了準(zhǔn)確測量冷啟動(dòng)耗時(shí)供置,測量前需要重啟設(shè)備。在main()方法執(zhí)行前測量是很難的绽快,好在dyld提供了內(nèi)建的測量方法:在 Xcode 中 Edit scheme -> Run -> Auguments 將環(huán)境變量DYLD_PRINT_STATISTICS設(shè)為1芥丧。

優(yōu)化啟動(dòng)時(shí)間

可以針對(duì) App 啟動(dòng)前的每個(gè)步驟進(jìn)行相應(yīng)的優(yōu)化工作。

加載 Dylib

之前提到過加載系統(tǒng)的 dylib 很快坊罢,因?yàn)橛袃?yōu)化续担。但加載內(nèi)嵌(embedded)的 dylib 文件很占時(shí)間,所以盡可能把多個(gè)內(nèi)嵌 dylib 合并成一個(gè)來加載活孩,或者使用 static archive物遇。使用dlopen()來在運(yùn)行時(shí)懶加載是不建議的,這么做可能會(huì)帶來一些問題憾儒,并且總的開銷更大询兴。

Rebase/Binding

之前提過 Rebaing 消耗了大量時(shí)間在 I/O 上,而在之后的 Binding 就不怎么需要 I/O 了起趾,而是將時(shí)間耗費(fèi)在計(jì)算上诗舰。所以這兩個(gè)步驟的耗時(shí)是混在一起的。

之前說過可以從查看__DATA段中需要修正(fix-up)的指針训裆,所以減少指針數(shù)量才會(huì)減少這部分工作的耗時(shí)眶根。對(duì)于 ObjC 來說就是減少Class,selector和category這些元數(shù)據(jù)的數(shù)量。從編碼原則和設(shè)計(jì)模式之類的理論都會(huì)鼓勵(lì)大家多寫精致短小的類和方法边琉,并將每部分方法獨(dú)立出一個(gè)類別属百,其實(shí)這會(huì)增加啟動(dòng)時(shí)間。對(duì)于 C++ 來說需要減少虛方法变姨,因?yàn)樘摲椒〞?huì)創(chuàng)建 vtable族扰,這也會(huì)在__DATA段中創(chuàng)建結(jié)構(gòu)。雖然 C++ 虛方法對(duì)啟動(dòng)耗時(shí)的增加要比 ObjC 元數(shù)據(jù)要少,但依然不可忽視渔呵。最后推薦使用 Swift 結(jié)構(gòu)體怒竿,它需要 fix-up 的內(nèi)容較少。

ObjC Setup

針對(duì)這步所能事情很少厘肮,幾乎都靠 Rebasing 和 Binding 步驟中減少所需 fix-up 內(nèi)容。因?yàn)榍懊娴墓ぷ饕矔?huì)使得這步耗時(shí)減少睦番。

Initializer

顯式初始化

使用+initialize來替代+load

不要使用__atribute__((constructor))將方法顯式標(biāo)記為初始化器类茂,而是讓初始化方法調(diào)用時(shí)才執(zhí)行。比如使用dispatch_once(),pthread_once()或std::once()托嚣。也就是在第一次使用時(shí)才初始化巩检,推遲了一部分工作耗時(shí)。

隱式初始化

對(duì)于帶有復(fù)雜(non-trivial)構(gòu)造器的 C++ 靜態(tài)變量:

在調(diào)用的地方使用初始化器示启。

只用簡單值類型賦值(POD:Plain Old Data)兢哭,這樣靜態(tài)鏈接器會(huì)預(yù)先計(jì)算__DATA中的數(shù)據(jù),無需再進(jìn)行 fix-up 工作夫嗓。

使用編譯器 warning 標(biāo)志-Wglobal-constructors來發(fā)現(xiàn)隱式初始化代碼迟螺。

使用 Swift 重寫代碼,因?yàn)?Swift 已經(jīng)預(yù)先處理好了舍咖,強(qiáng)力推薦矩父。

不要在初始化方法中調(diào)用dlopen(),對(duì)性能有影響排霉。因?yàn)閐yld在 App 開始前運(yùn)行窍株,由于此時(shí)是單線程運(yùn)行所以系統(tǒng)會(huì)取消加鎖,但dlopen()開啟了多線程攻柠,系統(tǒng)不得不加鎖球订,這就嚴(yán)重影響了性能,還可能會(huì)造成死鎖以及產(chǎn)生未知的后果瑰钮。所以也不要在初始化器中創(chuàng)建線程冒滩。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市浪谴,隨后出現(xiàn)的幾起案子旦部,更是在濱河造成了極大的恐慌,老刑警劉巖较店,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件士八,死亡現(xiàn)場離奇詭異,居然都是意外死亡梁呈,警方通過查閱死者的電腦和手機(jī)婚度,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蝗茁,你說我怎么就攤上這事醋虏。” “怎么了哮翘?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵颈嚼,是天一觀的道長。 經(jīng)常有香客問我饭寺,道長阻课,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任艰匙,我火速辦了婚禮限煞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘员凝。我一直安慰自己署驻,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布健霹。 她就那樣靜靜地躺著旺上,像睡著了一般。 火紅的嫁衣襯著肌膚如雪糖埋。 梳的紋絲不亂的頭發(fā)上抚官,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音阶捆,去河邊找鬼凌节。 笑死,一個(gè)胖子當(dāng)著我的面吹牛洒试,可吹牛的內(nèi)容都是我干的倍奢。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼垒棋,長吁一口氣:“原來是場噩夢啊……” “哼卒煞!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起叼架,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤畔裕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后乖订,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扮饶,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年乍构,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了甜无。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖岂丘,靈堂內(nèi)的尸體忽然破棺而出陵究,到底是詐尸還是另有隱情,我是刑警寧澤奥帘,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布铜邮,位于F島的核電站,受9級(jí)特大地震影響寨蹋,放射性物質(zhì)發(fā)生泄漏松蒜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一钥庇、第九天 我趴在偏房一處隱蔽的房頂上張望牍鞠。 院中可真熱鬧咖摹,春花似錦评姨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至店读,卻和暖如春嗦枢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背屯断。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國打工文虏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人殖演。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓氧秘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親趴久。 傳聞我的和親對(duì)象是個(gè)殘疾皇子丸相,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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