《深入理解Java虛擬機》讀書筆記2--垃圾收集(GC)與內(nèi)存分配

垃圾收集(Garbage Collection擦囊,GC)瞬场,其實主要需要完成3件事情:哪些內(nèi)存需要回收涧郊?什么時候回收?如何回收彤灶?

對于程序計數(shù)器幌陕、虛擬機棧以及本地方法棧汽煮,這三塊內(nèi)存區(qū)域是線程私有的暇赤,伴線程生,隨線程死止后,并且每一個棧幀需要的內(nèi)存在類結構確定后基本就確定了坯门,因此對于這幾塊區(qū)域逗扒,內(nèi)存的分配以及回收具備可確定性欠橘,當方法結束或者線程結束時,內(nèi)存隨之回收

但是對于堆以及方法區(qū)叉袍,運行時才能確定需要創(chuàng)建哪些對象刽酱,因此內(nèi)存分配具備動態(tài)性以及不確定性棵里。GC主要考慮的也是這部分區(qū)域的內(nèi)存回收

對象測活

所謂的“對象測活”,也就是要確定哪些對象已經(jīng)“死亡”典蝌,需要被回收

1.堆

對于堆中對象的測活骏掀,主要有兩種方法:引用計數(shù)柱告、可達性分析

#引用計數(shù)

主要思想是:給對象添加一個引用計數(shù)器际度,每當有一個地方引用該對象時,計數(shù)器+1眶熬;當引用失效時块请,計數(shù)器-1;當計數(shù)器為0的時候贸弥,說明該對象已經(jīng)沒有被引用绵疲,可以被回收

大多數(shù)時候臣疑,該算法足夠簡單讯沈、高效,但是缺點也顯而易見:不能處理循環(huán)引用的情況问慎。比如對象1引用對象2如叼,對象2也引用對象1,但是1和2已經(jīng)沒有被其他地方引用踊沸,因此這兩個對象實際上應該被回收雕沿。但是由于他們互相持有對方的引用审轮,彼此的引用計數(shù)器都不為0辽俗,所以不能被回收

#可達性分析

核心思想是:有一系列被稱為“GC Roots”的對象崖飘,從這些對象開始向下搜索朱浴,走過的路徑稱為引用鏈翰蠢。當一個對象沒有被任何鏈路所引用,則判定此對象是不可達的檀何,可以被回收

Java中可以被當作GC Roots的對象包括:虛擬機棧中引用的對象频鉴、方法區(qū)中靜態(tài)屬性引用的對象垛孔、方法區(qū)中常量引用的對象施敢、本地方法棧中JNI引用的對象

2.引用類型

Java提供了4種類型的引用:強引用(Strong Reference)悯姊、軟引用(Soft Reference)悯许、弱引用(Weak Reference)和虛引用(Phantom Reference)

之所以提供這么多的引用類型先壕,主要目的是為了滿足這類需求:對于一類對象,在內(nèi)存充足的時候集绰,則可以保存在內(nèi)存中栽燕;但是當GC后內(nèi)存依然捉襟見肘的時候改淑,則可以拋棄這類對象朵夏,釋放內(nèi)存仰猖。比較常見的一種使用場景是緩存

#強引用

強引用是最常見,也是使用最頻繁的一種引用鸵赫,通過“Object obj = new Object()”創(chuàng)建的就是強引用奉瘤,只要引用還存在盗温,對象就不會被回收

#軟引用

對于軟引用引用的對象成肘,在系統(tǒng)將要OOM之前双霍,會將這類對象納入回收范圍之內(nèi)進行回收,如果這次回收后依然沒有足夠內(nèi)存均芽,才會OOM单鹿。Java提供SoftReference類來實現(xiàn)軟引用

#弱引用

弱引用的引用強度低于軟引用仲锄,被弱引用引用的對象儒喊,只能存活到下一次GC前怀愧。當GC發(fā)生時,無論當前內(nèi)存是否充足肛搬,被弱引用引用的對象都會被回收温赔。Java提供WeakReference類來實現(xiàn)弱引用

#虛引用

虛引用是強度最弱的一種引用鬼癣,既不能通過虛引用來獲得對象實例,也不會對其生存時間構成影響拜秧。唯一的作用就是在這個對象被回收之前能通收到一個系統(tǒng)通知枉氮。Java提供PhantomReference類來實現(xiàn)虛引用

3.回收方法區(qū)

方法區(qū)(HotSpot虛擬機的永久代)的GC主要是回收兩部分內(nèi)容:廢棄的常量和無用的類

#廢棄的常量

廢棄常量的判定與堆中對象的測活十分相似聊替,只要沒有地方再引用到這個常量培廓,這個常量就被判定為“廢棄”肩钠,是可被回收的

#無用的類

類是否無用,判定條件相對苛刻呛每,需要同時滿足3個條件:

1)該類所有的實例已經(jīng)被回收

2)加載該類的ClassLoader已經(jīng)被回收

3)該類對應的java.lang.Class對象沒有在任何地方被引用莉给,也就是不能通過反射訪問

對于大量使用反射、動態(tài)代理徐矩、CGLib滤灯、動態(tài)生成JSP以及OSGi這類頻繁自定義ClassLoader的場景鳞骤,需要虛擬機具備類卸載功能豫尽,以避免方法區(qū)(永久代)溢出

GC算法

1.標記清除算法

“標記-清除(Mark-Sweep)”算法是最基礎的GC算法美旧,整個過程包括“標記”和“清除”兩個階段。主要不足有兩點:

#標記和清除妄呕,這兩個階段效率都不高

#產(chǎn)生大量內(nèi)存碎片绪励,導致為大對象分配內(nèi)存時疏魏,可能會提前觸發(fā)一次GC

2.復制算法

復制算法可以解決標記清除算法的兩個缺點蠢护,核心思想是:把內(nèi)存一分為二(容量相同)葵硕,每次只使用其中的一塊懈凹。當一塊內(nèi)存用完后介评,將仍然存活的對象復制到另一塊上,然后再將這塊內(nèi)存空間完整的清理掉

優(yōu)點:簡單高效寒瓦、無內(nèi)存碎片

缺點:每次只能使用一半內(nèi)存杂腰,50%的內(nèi)存被“浪費”

現(xiàn)代的虛擬機采用這種算法回收新生代喂很。新生代的對象大多“朝生夕死”少辣,因此并不需要按照1:1的比例劃分內(nèi)存漓帅,而是將一塊較大的內(nèi)存分配個Eden區(qū)煎殷,另外將兩塊較小的內(nèi)存分配給Survivor區(qū)

GC時豪直,將Eden和正在使用的一塊Survivor中仍然存活的對象復制到另一塊Survivor中弓乙,然后將Eden和剛才在使用的那塊Survivor空間一起回收掉暇韧。在復制過程中浓瞪,如果這塊Survivor空間不足乾颁,那么這部分對象將通過“分配擔保”湿右,直接進入老年代

HotSpot虛擬機默認將Eden和Survivor比例設置為8:1(兩塊Survivor各占10%)毅人,也就是新生代中每次可用內(nèi)存為90%丈莺,只有10%會被“浪費”

3.標記整理算法

復制算法在對象存活率較高時场刑,會進行大量的內(nèi)存復制,效率會降低邀桑。另外還需要額外的空間進行“分配擔北诨”捏萍,以應對對象全部存活的極端情況令杈。因此復制算法并不適用于老年代

標記整理算法與標記清除算法在標記階段一樣逗噩,但后續(xù)并不直接清理內(nèi)存异雁,而是將存活的對象向一側移動僧须,之后再清理掉邊緣以外的內(nèi)存

4.分代收集算法

分代收集算法嚴格來說并不是什么新算法示绊,核心思想是將堆劃分為新生代和老年代耻台,根據(jù)新生代和老年代不同的特點盆耽,采用不同的GC算法

新生代對象朝生夕死摄杂,存活率較低,采用復制算法墨坚,只需要少量的對象復制泽篮,就可以完成垃圾收集帽撑;老年代對象存活較久亏拉,并且沒有額外空間進行分配擔保及塘,所以通常采用標記清除或者標記整理算法

HotSpot中GC算法實現(xiàn)

1.枚舉根節(jié)點

現(xiàn)代應用內(nèi)存分配較多笙僚,如果完全對GC Roots進行枚舉往往需要很多時間溪烤。另外在此期間味咳,為了保證分析結果的準確性,需要暫停所有用戶線程(Stop The World檬嘀,STW)

目前主流的虛擬機都采用準確式GC槽驶,因此并不需要一個不漏的檢查完所有執(zhí)行上下文和全局的引用位置,虛擬機應當是有辦法得知哪些地方存放著對象引用的鸳兽。在HotSpot中掂铐,使用稱為OopMap的數(shù)據(jù)結構來達到上述目的。在類加載完成的時候全陨,虛擬機會把對象內(nèi)什么偏移量上是什么數(shù)據(jù)類型計算出來爆班。這樣在GC時就可以得知這些信息了

2.安全點

通過OopMap,HotSpot虛擬機可以快速準確的完成根節(jié)點枚舉辱姨。但是如果為每條指令都生成OopMap柿菩,那么GC的空間成本將會大大增加

實際上,只會在稱為安全點(Safe Point)的特定位置記錄這些信息雨涛,也就是說GC并非隨時都可運行枢舶,只有到達安全點時才可以。安全點太少替久,會導致GC等待時間太長凉泄;過多,會過分增加系統(tǒng)負載蚯根。單條指令執(zhí)行太快后众,所以不可能在任何指令后都設置安全點,而是要選擇“能夠讓程序長時間運行”的點颅拦,符合這種特征的點有:方法調(diào)用結束前蒂誉、循環(huán)末尾、可能發(fā)生異常的位置等

有了安全點距帅,下面面臨的一個問題是拗盒,如何在GC發(fā)生時讓所有線程都跑到安全點,主要有兩種方式:搶先式中斷和主動式中斷

#搶先式中斷

搶先式中斷锥债,就是在GC發(fā)生時,搶先中斷所有線程痊臭,如果發(fā)現(xiàn)有線程不在安全點哮肚,那么恢復該線程,讓他跑到安全點广匙。但是現(xiàn)在幾乎沒有虛擬機采用此方式

#主動式中斷

主動式中斷允趟,就是在GC發(fā)生時,不直接對線程進行操作鸦致,而是僅僅設置一個標志位潮剪,讓各個線程主動去輪詢,當線程發(fā)現(xiàn)這個標志時分唾,把自己中斷掛起抗碰。輪詢標志會被設置在和安全點重合的位置,或者創(chuàng)建對象時需要分配內(nèi)存的位置绽乔,因此線程中斷掛起的位置正好在安全點上

3.安全區(qū)域

無論采用上述哪種中斷方式弧蝇,都需要線程自己跑到安全點。但是對于例如處于Sleep或者Blocked狀態(tài)的線程,顯然是沒有辦法自己跑到安全點的看疗,這時候就需要安全區(qū)域(Safe Region)

如果在一段代碼中沙峻,引用關系不會發(fā)生變化,也就是說在這個區(qū)域內(nèi)是隨時可以開始GC的两芳,那么這段區(qū)域就可以視為安全區(qū)域

線程在執(zhí)行到安全區(qū)域時摔寨,首先會標記自己已經(jīng)進入安全區(qū)域亭饵,當GC發(fā)生時紧武,系統(tǒng)就不用管這些已經(jīng)進入安全區(qū)域的線程了未舟。當線程需要離開安全區(qū)域的時候闰靴,會檢查系統(tǒng)是否已經(jīng)完成了根節(jié)點枚舉甚至完成了整個GC過程超埋,如果沒有完成旭旭,那么線程就會等待寄狼,直到收到可以離開安全區(qū)域的信號為止

垃圾收集器

首先需要說明的一點是枫振,沒有最好的或者萬能的收集器斑鼻,只應當選擇最適合使用場景的收集器

1.Serial及Serial Old收集器

Serial收集器是最基本蒋纬、最簡單的收集器,歷史也最為悠久坚弱,采用復制算法蜀备,用于新生代GC。Serial Old則是其老年代版本荒叶,采用標記整理算法

如其名碾阁,它是一款單線程收集器,在工作時需要暫停其他所有線程些楣。如果需要掃描的內(nèi)存很大脂凶,STW將是一場災難

2.ParNew收集器

ParNew收集器實際上就是Serial收集器的多線程版本,使用多線程進行GC愁茁。由于多線程切換的開銷蚕钦,因此在單CPU的環(huán)境下,ParNew不會比Serial有更好的表現(xiàn)鹅很。但是對于多CPU(或者多核)環(huán)境來說嘶居,ParNew則可以更好的利用CPU資源進行GC。默認情況下促煮,它的GC線程數(shù)與CPU數(shù)量相同邮屁,可通過啟動參數(shù)-XX:ParallelGCThreads調(diào)節(jié)

3.Parallel Scavenge及Parallel Old收集器

Parallel Scavenge收集器是一個新生代收集器,采用復制算法菠齿,用于新生代GC佑吝。Parallel Old則是其老年代版本,采用標記整理算法泞当。

與其他更加關注用戶線程停頓時間的收集器(比如CMS收集器)不同迹蛤,Parallel Scavenge收集器更加注重可控的吞吐量(換句話說就是GC耗費時間的占比)

Parallel Scavenge收集器提供兩個參數(shù)控制吞吐量:-XX:MaxGCPauseMillis和-XX:GCTimeRatio

#MaxGCPauseMillis

MaxGCPauseMillis參數(shù)使收集器盡可能保證GC在設定時間內(nèi)完成民珍。這個值并不是越小越好,因為這個值設置的小盗飒,并不代表GC速度會更快嚷量。相反,GC停頓時間縮短是以犧牲吞吐量和新生代空間換取來的逆趣。新生代小了當然單次回收會更快蝶溶,但是GC的頻次卻會增加

#GCTimeRatio

GCTimeRatio參數(shù)設置的是GC時間占比,比如設置為19宣渗,那么允許的最大GC時間占比就是1/20抖所,系統(tǒng)默認值是99

由于與吞吐量密切相關,所以Parallel Scavenge收集器也被稱為“吞吐量優(yōu)先”收集器痕囱。除了上述兩個參數(shù)外田轧,-XX:+UseAdaptiveSizePolicy也值得關注。UseAdaptiveSizePolicy是一個開關參數(shù)鞍恢,當開啟時傻粘,就無需再手動設置新生代大小、Eden和Survivor的比例等細節(jié)參數(shù)帮掉,虛擬機會根據(jù)系統(tǒng)的實際運行監(jiān)控數(shù)據(jù)弦悉,動態(tài)的調(diào)整這些參數(shù)以提供合適的GC提頓時間或者吞吐率

4.CMS收集器

在介紹Parallel Scavenge收集器的時候,我們提到CMS收集器是更加關注用戶線程停頓時間的收集器蟆炊。這種特征更加適用于諸如互聯(lián)網(wǎng)服務這類尤其重視服務響應速度的需求

CMS全稱Concurrent Mark Sweep稽莉,可以看出其基于標記清除算法,整體來看分為4個步驟:初始標記涩搓、并發(fā)標記污秆、重新標記以及并發(fā)清除。其中初始標記和重新標記階段仍然需要STW

#初始標記昧甘,僅僅只是標記一下GC Roots能直接關聯(lián)到的對象混狠,速度很快

#并發(fā)標記,就是進行根節(jié)點枚舉疾层,此過程通常較長

#重新標記,是將并發(fā)標記過程中因用戶線程產(chǎn)生變動的對象進行重新標記記錄贡避,比并發(fā)標記過程要短

#并發(fā)清除痛黎,執(zhí)行清除,可與用戶線程并非執(zhí)行

由于最耗時的并發(fā)標記和并發(fā)清除階段可以與用戶線程一起并發(fā)執(zhí)行刮吧,因此該收集器的執(zhí)行過程從整體上看是與用戶線程一起并發(fā)執(zhí)行的

CMS收集器優(yōu)點是并發(fā)收集湖饱、低停頓,但是缺點也很明顯:

#對CPU資源敏感杀捻。雖然在并發(fā)階段不會導致用戶線程停頓井厌,但是會占用一部分線程資源,導致系統(tǒng)處理能力降低

#無法處理浮動垃圾。所謂“浮動垃圾”就是指在并發(fā)清除階段仅仆,由于用戶線程并發(fā)運行所產(chǎn)生的垃圾器赞。這部分垃圾由于是出現(xiàn)在標記過程之后,因此在本次GC中是無法被收集掉的墓拜,只能等下次GC時處理港柜。由此可以看出,CMS在運行時需要留給用戶線程足夠的內(nèi)存空間正常運行咳榜,所以也就不能像其他收集器那樣等到內(nèi)存幾乎快滿了才開始GC夏醉。當CMS運行時,如果預留的內(nèi)存不能以滿足程序需求涌韩,將會觸發(fā)“Concurrent Mode Failure”畔柔,這時虛擬機將臨時啟用Serial Old收集器來進行GC,這個過程停頓就很長了臣樱,性能反而下降

#產(chǎn)生內(nèi)存碎片靶擦。基于標記清除算法的收集器都有這個問題擎淤。碎片過多時奢啥,將給大對象分配帶來很大麻煩,有時候老年代還有很大剩余空間嘴拢,但是由于一個大對象無法分配桩盲,不得不提前觸發(fā)一次GC。CMS提供了相關參數(shù)設置內(nèi)存碎片合并席吴,但合并過程是無法并發(fā)的赌结,將導致停頓時間變長

5.G1收集器

G1收集器面向服務器應用,相比其他收集器孝冒,具備以下特征:

#并發(fā)與并行柬姚。G1能夠充分利用多CPU(或者多核)的硬件優(yōu)勢來縮短STW時間,部分在其他收集器需要停頓用戶線程的操作庄涡,G1可通過并發(fā)的方式讓用戶線程繼續(xù)執(zhí)行

#分代收集量承。與其他收集器中分代收集(通過對新生代和老年代使用不同的收集器)不同的是,G1能夠獨自完成整個堆的GC穴店,它能夠對不同年齡的對象采用不同的方式處理

#空間整合撕捍。G1從整體上看采用標記整理算法,從局部來看(兩個Region之間)采用復制算法泣洞。這兩種算法可以保證不會產(chǎn)生內(nèi)存碎片忧风,GC后可提供規(guī)整的可用內(nèi)存

#可預測的停頓。G1提供可預測的停頓時間模型球凰,能夠明確指定在一段時間內(nèi)GC時間不超過指定時間

在使用G1時狮腿,雖然仍然保留新生代腿宰、老年代的概念,但堆內(nèi)存已經(jīng)被劃分為大小相同的獨立Region缘厢。新生代與老年代不再物理隔離吃度,而都是多個Region的集合(Region不要求連續(xù))

G1跟蹤各個Region里面垃圾收集的價值(回收空間與回收時間的經(jīng)驗值/性價比),在GC時優(yōu)先回收收益最高的Region昧绣,從而避免對整個堆內(nèi)存進行回收规肴。這就是G1能夠提供可預測的停頓時間模型的原因

G1的原理似乎很簡單,但是細節(jié)卻十分復雜夜畴,比如不同Region之間對象的引用問題拖刃,如果進行全堆掃描,效率會降低很多贪绘。

G1的執(zhí)行大致分為幾個步驟:初始標記兑牡、并發(fā)標記、最終標記税灌、篩選回收

#初始標記均函,僅僅只是標記一下GC Roots能直接關聯(lián)到的對象。雖然需要停頓用戶線程菱涤,但是速度很快

#并發(fā)標記苞也,就是進行根節(jié)點枚舉。此過程通常較長粘秆,但是可以與用戶線程并發(fā)執(zhí)行

#最終標記如迟,是將并發(fā)標記過程中因用戶線程產(chǎn)生變動的對象進行重新標記記錄。此過程需要停頓用戶線程攻走,但是可以并行執(zhí)行

#篩選回收殷勘,根據(jù)設定的期望停頓時間,對各Region回收收益進行權衡昔搂,然后回收內(nèi)存玲销。此過程需要停頓用戶線程,但是由于只需要回收一部分Region摘符,時間可控贤斜,并且停頓用戶線程可以大幅提高回收效率

對象的內(nèi)存分配

對象內(nèi)存的分配,從整體上看逛裤,是在堆上分配蠢古。對象被分配在Eden區(qū),如果啟用了TLAB别凹,將分配在TLAB上,少數(shù)情況下還可能直接分配在老年代中洽糟。規(guī)則不是完全固定的炉菲,細節(jié)取決于使用的GC收集器組合以及JVM中內(nèi)存相關的參數(shù)設置堕战。接下來講解幾條基本的、普遍的分配規(guī)則

1.對象優(yōu)先分配在Eden

大多數(shù)情況下拍霜,對象優(yōu)先在Eden上分配嘱丢,當Eden內(nèi)存不足時,將觸發(fā)一次Minor GC

2.大對象直接進入老年代

大對象祠饺,指的是需要很大連續(xù)內(nèi)存空間的對象越驻。大量創(chuàng)建大對象,對虛擬機來說是個壞消息道偷,因為很容易導致雖然還有很大內(nèi)存缀旁,但是卻沒有連續(xù)內(nèi)存存放大對象,從而提前觸發(fā)GC勺鸦,另外也會導致新生代GC過程中在Eden和兩個Survivor區(qū)之間大量的內(nèi)存復制操作

虛擬機提供-XX:PretenureSizeThreshold參數(shù)來設置需要內(nèi)存超過一定閾值的大對象將直接分配在老年代

3.長期存活的對象將進入老年代

哪些對象該進入老年代并巍?這就引入了一個age的概念,虛擬機會給每個對象定義一個對象年齡换途。如果對象在Eden中經(jīng)過第一次Minor GC后仍然存活懊渡,并且能夠被Survivor容納,那么它將被轉移到Survivor中军拟,并且age=1剃执。之后每熬過一次Minor GC,age增加1歲懈息。當age達到一定程度(默認15肾档,可通過-XX:MaxTenuringThreshold設置)后,對象將會被晉升到老年代

但是漓拾,對象并非需要嚴格熬到MaxTenuringThreshold才能晉升到老年代阁最。如果在Survivor空間中相同年齡所有對象占用內(nèi)存總和大于Survivor空間的一半時,年齡大于等于該年齡的對象就會直接被晉升到老年代

4.空間分配擔保

在進行Minor GC之前骇两,虛擬機會檢查老年代最大可用連續(xù)空間是否大于新生代所有對象的總空間速种。如果大于,說明這次Minor GC就是安全的低千。否則配阵,會檢查是否允許擔保失敗。如果不允許示血,那么進行Full GC棋傍。否則,繼續(xù)檢查老年代最大可用連續(xù)空間是否大于歷次晉升到老年代對象的平均大小难审。如果小于瘫拣,依然進行Full GC。否則將嘗試進行Minor GC告喊,但這次Minor GC是有風險的

上面提到了風險麸拄,由于新生代采用復制算法進行Minor GC派昧,每次只有一個Survivor區(qū)(默認10%)用于空間輪換,自然無法100%保證有足夠空間用于輪換拢切,因此就需要老年代為此提供擔保

與現(xiàn)實中的擔保一樣蒂萎,擔保人需要有足夠的能力保證被擔保人無力償還貸款時自己有能力代為償還。老年代也需要保證自己有足夠的空間容納這些對象淮椰,但是會有多少對象存活在GC前是無法知道的五慈,因此會取歷次晉升到老年代對象的平均大小作為經(jīng)驗值,與老年代最大可用連續(xù)空間做比較主穗,再決定是否需要進行Full GC

這種手段是一種概率手段泻拦,既然是概率手段,那么終歸會有失敗的可能黔牵。如果失敗了聪轿,那么就將觸發(fā)一次Full GC。但是在大多數(shù)情況下猾浦,F(xiàn)ull GC并不會發(fā)生陆错。因此從概率上來看,這種方案有助于性能提升

思維導圖:

筆記2結束

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末金赦,一起剝皮案震驚了整個濱河市音瓷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌夹抗,老刑警劉巖绳慎,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異漠烧,居然都是意外死亡杏愤,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門已脓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來珊楼,“玉大人,你說我怎么就攤上這事度液〔拮冢” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵堕担,是天一觀的道長已慢。 經(jīng)常有香客問我,道長霹购,這世上最難降的妖魔是什么佑惠? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上膜楷,老公的妹妹穿的比我還像新娘乍丈。我一直安慰自己,他們只是感情好把将,可當我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著忆矛,像睡著了一般察蹲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上催训,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天洽议,我揣著相機與錄音,去河邊找鬼漫拭。 笑死亚兄,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的采驻。 我是一名探鬼主播审胚,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼礼旅!你這毒婦竟也來了膳叨?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤痘系,失蹤者是張志新(化名)和其女友劉穎菲嘴,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體汰翠,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡龄坪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了复唤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片健田。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖苟穆,靈堂內(nèi)的尸體忽然破棺而出抄课,到底是詐尸還是另有隱情,我是刑警寧澤雳旅,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布跟磨,位于F島的核電站,受9級特大地震影響攒盈,放射性物質(zhì)發(fā)生泄漏抵拘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一型豁、第九天 我趴在偏房一處隱蔽的房頂上張望僵蛛。 院中可真熱鬧尚蝌,春花似錦、人聲如沸充尉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽驼侠。三九已至姿鸿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間倒源,已是汗流浹背苛预。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留笋熬,地道東北人热某。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像胳螟,于是被迫代替她去往敵國和親昔馋。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,086評論 2 355

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