《深入理解Java虛擬機(jī)》讀書筆記(第三章)

概述


內(nèi)存回收主要考慮三件事情

  • 那些內(nèi)存需要回收?
  • 什么時(shí)候回收?
  • 如何回收?

程序計(jì)數(shù)器、虛擬機(jī)棧、本地方法棧三個(gè)區(qū)域隨線程而生,隨線程而滅,每一個(gè)棧幀中分配多少內(nèi)存基本上是在類結(jié)構(gòu)定下來時(shí)就已知的。因此這三個(gè)區(qū)域不需要過多的考慮回收的問題,因?yàn)楫?dāng)方法結(jié)束或者線程結(jié)束是,內(nèi)存自然就跟著回收了。

而Java堆和方法區(qū)則不一樣,一個(gè)接口中的多個(gè)實(shí)現(xiàn)類需要的內(nèi)存可能不一樣,一個(gè)方法中的多個(gè)分支需要的內(nèi)存也不一樣。而且我們只有在程序的運(yùn)行期間時(shí)才能知道創(chuàng)建哪些對象,這部分的內(nèi)存分配和回收都是動態(tài)的,垃圾收集器關(guān)注的也是這部分。

對象已死嗎?


引用計(jì)數(shù)算法

給對象添加一個(gè)一弄計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用它時(shí),計(jì)數(shù)器值就加1;當(dāng)引用失效時(shí),計(jì)數(shù)器值就減1;任何時(shí)刻計(jì)數(shù)器為0的對象就是不可能再被使用的。但是如果兩個(gè)對象互相引用,引用計(jì)數(shù)算法就無法判斷這兩個(gè)對象是否存活。

可達(dá)性分析算法

通過一系列稱為“GC Roots”的對象作為起始點(diǎn),從這些節(jié)點(diǎn)開始向下搜索,搜索走過的路徑名稱為引用鏈(Reference Chain),當(dāng)一個(gè)對象到GC Roots沒有任何引用鏈時(shí),則證明次對象不可用的。

在Java語言中,可作為GC Roots的對象包括下面幾種:

  • 虛擬機(jī)棧(棧幀中的本地變量表象)中引用的對。
  • 方法區(qū)中類靜態(tài)屬性引用的對象
  • 方法區(qū)中常量引用的對象
  • 本地方法棧中JNI(即一般說的Native方法)引用的對象

再談引用

  • 強(qiáng)引用:new出來的對象是強(qiáng)引用,只要強(qiáng)引用還存在,垃圾收集器永遠(yuǎn)不會回收掉被引用的對象。
  • 軟引用:在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前,將會把這些對象列進(jìn)回收范圍之中進(jìn)行第二次回收。如果這次回收后還是沒有足夠的內(nèi)存,才會拋出內(nèi)存溢出的異常。
  • 弱引用:被弱引用關(guān)聯(lián)的對象只能生存到下一次垃圾回收發(fā)生之前。當(dāng)垃圾收集器工作時(shí),無論當(dāng)前內(nèi)存是否足夠,都會回收掉紙杯弱引用關(guān)聯(lián)的對象。
  • 虛引用:它是最弱的一種關(guān)系,不會對生存時(shí)間構(gòu)成任何影響。為一個(gè)對象設(shè)置虛引用的唯一目的就是在被垃圾回收時(shí)收到一個(gè)通知。

生存還是死亡

真正宣告一個(gè)對象死亡,至少要進(jìn)行兩次標(biāo)記過程。

如果對象經(jīng)過可達(dá)性算法分析后,發(fā)現(xiàn)沒有與GC Roots相連的引用鏈,它會被第一次標(biāo)記和篩選。篩選的條件是:是否有必要執(zhí)行finalize()方法。當(dāng)且僅當(dāng)該對象覆蓋finalize()方法,并且沒有被虛擬機(jī)調(diào)用過,才能稱之為有必要執(zhí)行finalize()方法。

如果有必要執(zhí)行,虛擬機(jī)會把這個(gè)對象放在一個(gè)名字叫做F-Queue的隊(duì)列中執(zhí)行。但是并不承諾等待finalize()方法運(yùn)行結(jié)束。這是對象逃脫死亡命運(yùn)的最后一次機(jī)會,稍后GC會對這些對象進(jìn)行第二次小規(guī)模標(biāo)記,如果這些對象沒有拯救自己,那么基本上就被回收了。

PS:finalize()方法只會被執(zhí)行一次。另外,能用finalize()方法實(shí)現(xiàn)的功能用 try-finally或者其他的方式都能做的更好,所以針對finalize()方法可以完全不用。

回收方法區(qū)

方法區(qū)的回收主要是兩部分類容:廢棄常量和無用的類。

判定為無用的類要滿足以下三個(gè)條件:

  • 該類所有的實(shí)例都已經(jīng)被回收,也就是Java堆中不存在該類的任何實(shí)例。
  • 加載該類的ClassLoader已經(jīng)被回收
  • 該類對應(yīng)的java.lang.Class對象沒有任何地方被引用,無法在任何地方通過反射訪問該類的方法。

虛擬機(jī)可以對滿足上述3個(gè)條件的無用類進(jìn)行回收,這里僅僅是“可以”,而不是和對象一樣,不使用了就必然會回收。

垃圾收集算法


標(biāo)記-清除算法

算法分為“標(biāo)記”和“清除”兩個(gè)階段:首先標(biāo)記出所有需要回收的對象,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對象。

主要不足點(diǎn)有兩個(gè):

  • 效率問題,標(biāo)記和清除兩個(gè)過程的效率都不算高
  • 空間問題,標(biāo)記清除后會產(chǎn)生大量不連續(xù)的內(nèi)存隨便,空間碎片太多可能會導(dǎo)致以后在程序運(yùn)行的過程中需要分配較大的對象時(shí),找不到足夠的連續(xù)內(nèi)存而提前觸發(fā)另一次垃圾收集動作。

后續(xù)收集算法都是基于這種思路對其不足進(jìn)行改進(jìn)。

復(fù)制算法

將可用內(nèi)存按照容量劃分為大小相等的兩塊,每次只使用其中的一塊。當(dāng)這一塊內(nèi)存用完了,就將存貨的對象復(fù)制到另外一塊上面,然后把已使用的內(nèi)存空間一次清理掉。

不足點(diǎn):將內(nèi)存縮小為原來的一半為代價(jià)。

這種算法主要是來回收新生代(但不是1:1的比例來劃分內(nèi)存空間)。新生代是一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden和其中一個(gè)Survivor。當(dāng)GC時(shí),把Eden和Survivor還存活的對象一次性復(fù)制到另一個(gè)Survivor空間上,最后清理掉Eden空間和剛開用過的Survivor空間。

標(biāo)記-整理算法

標(biāo)記過程仍然與“標(biāo)記-清楚”算法一樣,但是后續(xù)步驟不是直接對可回收的對象進(jìn)行清理,而是讓所有已存活的對象都向一段移動,然后直接清理掉端便捷以外的內(nèi)存。

分代收集算法

分代收集算法并沒有新的思想。一般是把Java堆分為新生代和老年代,然后根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴āT谛律?,每次垃圾收集時(shí)有大批對象死去,只有少量對象存活,就采用復(fù)制算法。而老年代因?yàn)閷ο蟠婊盥瘦^高,而且沒有額外的空間對它進(jìn)行分配擔(dān)保,就必須使用“標(biāo)記-清除”或者“標(biāo)記-整理”算法。

HotSpot的算法實(shí)現(xiàn)


枚舉根節(jié)點(diǎn)(哪些內(nèi)存需要回收)

可作為GC Roots的節(jié)點(diǎn)主要在全局性的引用(常量貨類靜態(tài)屬性),執(zhí)行上下文(棧幀中的本地變量表)。

可達(dá)性分析對執(zhí)行時(shí)間的敏感主要是GC停頓上,GC進(jìn)行時(shí)必須停頓所有的Java執(zhí)行線程(Stop The World)。其實(shí)這里很好理解,就像媽媽在家里打掃房間,肯定是要求你坐在座位上別動,否則一邊打掃房間,而你到處走動并且時(shí)不時(shí)的亂丟東西,那么這個(gè)打掃房間是永遠(yuǎn)掃不完的。

但是現(xiàn)在很多應(yīng)用方法區(qū)就數(shù)百兆大小,如果逐個(gè)檢查這里面的引用,那必然會耗費(fèi)非常多的時(shí)間。虛擬機(jī)有沒有辦法解決這個(gè)問題呢?有的。在HotSpot的實(shí)現(xiàn)中,是使用一組稱為OopMap的數(shù)據(jù)結(jié)構(gòu)來達(dá)到這個(gè)目的的,在類加載完成的時(shí)候,HotSpot就把對象內(nèi)什么偏移量上是什么類型的數(shù)據(jù)計(jì)算出來,在JIT編譯過程中,也會在特定的位置記錄下棧和寄存器中哪些位置是引用。這樣,GC在掃描時(shí)就可以直接得知這些信息了。

安全點(diǎn)(什么時(shí)候回收)

在OopMap的協(xié)助下,HotSpot可以快速而且準(zhǔn)確的完成GC Roots的枚舉。但是也會存在一些問題。OopMap內(nèi)容變化的指令非常多的,如果每一個(gè)指令都去生成對應(yīng)的OopMap,那需要大量的額外空間,這樣GC的空間成本將會變得很高。

其實(shí)HotSpot也不會這么笨的,去給每條指令都生成OopMap,前面已經(jīng)提到,只是在特定的位置記錄了這些信息,這些位置稱之為安全點(diǎn)(Savepoint),即程序執(zhí)行時(shí)并非在所有的地方都能停頓下來GC,只有到達(dá)安全點(diǎn)才能停頓。

這里的停頓有兩種方式:

  • 搶先式中斷:在GC發(fā)生時(shí),首先把所有的線程全部中斷。如果發(fā)現(xiàn)有線程中斷的地方不在安全點(diǎn)上,就恢復(fù)線程,讓它“跑”到安全點(diǎn)上。現(xiàn)在機(jī)會沒有虛擬機(jī)采取搶先式中斷了。
  • 主動式中斷:當(dāng)GC需要中斷線程的時(shí)候,不直接對線程操作,僅僅簡單的設(shè)置一個(gè)標(biāo)志。哥哥線程執(zhí)行時(shí)主動去輪詢這個(gè)標(biāo)志,發(fā)現(xiàn)中斷標(biāo)志為真時(shí),就自己中斷掛起。

安全區(qū)域

如果有些線程是“不執(zhí)行狀態(tài)”(比如線程Sleep,或者Bolcked),這時(shí)候線程是無法相應(yīng)JVM的中斷請求,“走”到安全點(diǎn)去中斷掛起。對于這種情況,就需要安全區(qū)域(Safe Region)來解決。

我們可以把Safe Region看作是Safepoint的擴(kuò)展版。在這個(gè)區(qū)域中的任意地方,開始GC都是安全的。

在線程執(zhí)行到Safe Region中的代碼時(shí),首先標(biāo)識自己已經(jīng)進(jìn)入了Safe Region,當(dāng)這段時(shí)間JVM要發(fā)起GC時(shí),就不管標(biāo)識自己為Safe Region的線程了。當(dāng)線程要離開Safe Region時(shí),它會檢查系統(tǒng)是否完成了枚舉根節(jié)點(diǎn)(或者是GC的整個(gè)過程),如果完成了,線程就繼續(xù)執(zhí)行。否則就等待直到收到可以離開Safe Region的信號為止。

垃圾收集器


Serial收集器

新生代、單線程收集器。采用復(fù)制算法。它的“單線程”的意義并不僅僅說明它只會使用一個(gè)CPU或者一條收集線程去完成GC,更重要的是,它在GC時(shí),必須暫停其他所有的工作線程(Stop The World),知道它收集結(jié)束。

ParNew收集器

新生代收集器。相當(dāng)于Serial的多線程版本(并行的多線程收集器)。采用復(fù)制算法。也需要Stop The World。

Parallel Scavenge收集器

新生代收集器,使用多線程和復(fù)制算法,需要Stop The World,也被稱為“吞吐量優(yōu)先”收集器。

Parallel Scavenge收集器的目標(biāo)是達(dá)到一個(gè)可控制的吞吐量(Throughput)。所謂的吞吐量就是CPU用于運(yùn)行用戶代碼的時(shí)間與CPU總消耗時(shí)間的比值。即吞吐量=運(yùn)行用戶代碼時(shí)間/(運(yùn)行用戶代碼時(shí)間+垃圾收集時(shí)間)。

高吞吐量和低停頓對虛擬機(jī)來收是兩個(gè)矛盾的事。停頓時(shí)間越短就越適合需要與用戶交互的程序,良好的響應(yīng)速度能提升用戶體驗(yàn)。而高吞吐量則可以搞笑的利用CPU的時(shí)間,盡快完成運(yùn)算任何,主要適合在后臺運(yùn)算而不需要太多交互的任務(wù)。

Serial Old收集器

相當(dāng)于Serial收集器的老年代版本,單線程收集器,需要Stop The World,財(cái)務(wù)“標(biāo)記-整理”算法。

Parallel Old收集器

相當(dāng)于Parallel Scavenge收集器的老年代版本。使用多線程和“標(biāo)記-整理”算法。

CMS收集器

Concurrent Mark Sweep收集器是一種獲取最短回收停頓時(shí)間為目標(biāo)的老年代收集器,采用“標(biāo)記-清除”算法。整個(gè)過程分四個(gè)步驟:

  • 初始標(biāo)記:需要Stop The World,單線程,僅僅只是標(biāo)記一下GC Roots能直接關(guān)聯(lián)到的對象(不會遞歸)。
  • 并發(fā)標(biāo)記:不需要Stop The World,單線程,與用戶線程一起工作,進(jìn)行GC Roots Tracing的過程。
  • 重新標(biāo)記:需要Stop The World,多線程工作,目的是為了修正并發(fā)標(biāo)記期間,因用戶程序繼續(xù)運(yùn)作而導(dǎo)致系統(tǒng)標(biāo)記產(chǎn)生變動的那一部分對象的標(biāo)記記錄。
  • 并發(fā)清理:不需要Stop The World,單線程,清除GC Roots不可達(dá)對象。

但是CMS收集器有一下3個(gè)明顯的缺點(diǎn):

  • CMS收集器對CPU資源非常敏感。在并發(fā)階段,它雖然不會Stop The World,但是會因?yàn)檎加昧艘徊糠志€程而導(dǎo)致應(yīng)用程序變慢,總吞吐量降低。
  • CMS收集器無法處理浮動垃圾,可能出現(xiàn)“ConCurrent Mode Failure”失敗而導(dǎo)致另一次Full GC的產(chǎn)生。由于CMS并發(fā)清理階段用戶線程還在繼續(xù)運(yùn)行,所以自然就會有新的垃圾不斷產(chǎn)生,出現(xiàn)在標(biāo)記過程之后產(chǎn)生的垃圾,在本次收集中CMS無法處理處理掉他們,只能等待下一次GC再解決。這部分稱之為“浮動垃圾”。也是由于垃圾手機(jī)階段用戶線程還要繼續(xù)運(yùn)行,所以還需要預(yù)留足夠的內(nèi)存空間給用戶線程使用,CMS無法像其他收集器那樣等老年代幾乎被填滿再GC。
  • CMS采用的是“標(biāo)記-清除”算法,所有可能會有大量空間碎片產(chǎn)生。往往老年代還有很大空間剩余,但是卻沒有足夠大的連續(xù)空間來分配當(dāng)前對象,這時(shí)候就不得不提前觸發(fā)一次Full GC。

G1收集器

Garbage-First收集器。它具有以下特點(diǎn):

  • 并行與并發(fā):G1手機(jī)起可以通過并發(fā)的方式讓Java程序繼續(xù)執(zhí)行。
  • 分代收集:分代的概念在G1中仍然得以保留。G1收集器不需要其他收集器那樣互相配合管理整個(gè)GC堆,它自身可以用不同的方式去處理新創(chuàng)建的對象和已經(jīng)存活一段時(shí)間的對象、熬過多次GC的就對象。
  • 空間整合:從整體上看G1收集器是基于“標(biāo)記-整理”算法實(shí)現(xiàn)的,從局部(兩個(gè)Region)之間上來看是基于“復(fù)制”算法實(shí)現(xiàn)的。無論如何,這意味著G1收集器不會產(chǎn)生空間碎片。
  • 可預(yù)測的停頓:除了追求低停頓,G1還能簡歷可預(yù)測的停頓時(shí)間建模,能讓使用者明確指定在一個(gè)長度為M毫秒的時(shí)間片段內(nèi),消耗在GC上的時(shí)間不得超過N秒。

G1不再像其他收集器那樣使用新生代和老年代,它的內(nèi)存布局余其他的收集器有很大的差別,它將Java堆劃分為多個(gè)大小相等的獨(dú)立區(qū)域(Region),雖然它還保留著新生代和老年代的概念,但是新生代和老年代不再是物理隔離的,它們都是一部分Region的集合。

G1跟蹤各個(gè)Region里面的垃圾堆積的價(jià)值大小,在后臺維護(hù)一個(gè)優(yōu)先列表,每次根據(jù)允許手機(jī)的時(shí)間,優(yōu)先回收價(jià)值最大的Region。

G1收集器中,Region之間的對象引用以及其他收集器中新生代與老年代之間的對象引用,虛擬機(jī)都是使用Remembered Set來避免全堆掃描的。G1中每個(gè)Region都有一個(gè)與之對應(yīng)的Remeber Set,當(dāng)Reference類型的數(shù)據(jù)引用的對象處于不同的Region之中(分代的例子就是老年代中的對象引用了新生代的對象),那么就會把信息記錄到被引用對象所屬的Region的Remebered Set中。當(dāng)GC時(shí),在GC根節(jié)點(diǎn)的枚舉范圍中加入Remebered Set既可保證不對全堆掃描。

如果不記維護(hù)Remebered Set的操作,G1收集器可以分為以下幾個(gè)步驟:

  • 初始標(biāo)記:需要Stop The World,單線程,僅僅只是標(biāo)記一下GC Roots能直接關(guān)聯(lián)到的對象(不會遞歸)。
  • 并發(fā)標(biāo)記:不需要Stop The World,單線程,與用戶線程一起工作,進(jìn)行GC Roots Tracing的過程。
  • 最終標(biāo)記:需要Stop The World,多線程工作,目的是為了修正并發(fā)標(biāo)記期間,因用戶程序繼續(xù)運(yùn)作而導(dǎo)致系統(tǒng)標(biāo)記產(chǎn)生變動的那一部分對象的標(biāo)記記錄。
  • 最終篩選:需要 Stop The World(和CMS不同),多線程,清除GC Roots不可達(dá)對象。根據(jù)SUN公司透露的信息,原本這個(gè)階段也是可以和用戶程序一起并發(fā)執(zhí)行的,但是因?yàn)橹皇腔厥找徊糠諶egion,GC時(shí)間可控制,而且停頓用戶線程也可以大幅度提高GC效率,所以是需要Stop The World。

內(nèi)存分配與回收的策略


對象主要分配在新生代的Eden區(qū)上,如果啟動了本地線程分配緩沖,則按線程優(yōu)先在TLAB上分配,少數(shù)情況也可能直接分配在老年代中。分配規(guī)則不是100%固定的,取決于當(dāng)前使用的是哪一種垃圾收集器的組合,還有虛擬機(jī)與內(nèi)存相關(guān)的參數(shù)設(shè)置。

  • 新生代GC(Minor GC):指發(fā)生在新生代的垃圾手機(jī)動作,因?yàn)镴ava對象大多數(shù)都具有朝生夕滅的特性,所以Minor GC非常頻繁,一般回收速度也比較快。
  • 老年代GC(Major GC/Full GC):指發(fā)生在老年代的GC,出現(xiàn)了Major GC,經(jīng)常會伴隨至少一次的Minor GC。Major GC的速度一般會比Minor GC慢10倍以上。

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

大多數(shù)情況下,對象在新生代Eden區(qū)中分配。當(dāng)Eden區(qū)沒有足夠的空間進(jìn)行分配時(shí),虛擬機(jī)將發(fā)生一次Minor GC。

大對象直接進(jìn)入老年代

所謂的大對象是指,需要大量連續(xù)內(nèi)存空間的Java對象,最典型的大對象就是那種很長的字符串以及數(shù)組。大對象對與你及的內(nèi)存分配來說,就是一個(gè)壞消息(更壞的消息,就是一群“朝生夕滅”的“短命大對象”,寫程序時(shí)應(yīng)該盡量避免)。經(jīng)常出現(xiàn)大對象容易導(dǎo)致內(nèi)存還有不少空間時(shí),就提前出發(fā)垃圾收集以獲取足夠的連續(xù)空間來“安置”他們。

長期存貨的對象將進(jìn)入老年代

虛擬機(jī)給每個(gè)對象定義一個(gè)對象年齡(Age)計(jì)數(shù)器。如果對象在Eden區(qū)出生,并經(jīng)過第一次Minor GC后仍然存貨,并且能夠被Survivor容納,將被移動到Survivor空間中,并且對象年齡設(shè)為1。對象在Survivor區(qū)中每“熬過”一次Minor GC,年齡就增加1歲,當(dāng)它的年齡增加到一定程度(默認(rèn)15歲),就會被晉升到老年代中。

動態(tài)對象年齡判定

如果Survivor空間中相同年齡所有對象大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的對象就可以直接進(jìn)入老年代。

空間分配擔(dān)保

在發(fā)生Minor GC之前,虛擬機(jī)先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對象的總空間,如果大于,那么Minor GC可以確保是安全的。如果不大于,則虛擬機(jī)會查看HandlePromotionFailure設(shè)置值是否允許擔(dān)保失敗。如果不允許,這是要進(jìn)行一次Full GC。如果允許,那么會繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對象的平均大小,如果大于則進(jìn)行一次冒險(xiǎn)的Minor GC。如果小于,則進(jìn)行一次Full GC。

大部分情況下,還是會將HandlePromotionFailure開關(guān)打開,避免Full GC過于頻繁。

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

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