JVM的垃圾回收

一堤结、簡述

GC(Garbage Collection):Java/.NET 中的垃圾收集器唆迁。Java 是由 C++ 發(fā)展來的。它擯棄了 C++ 中一些繁瑣容易出錯的東西竞穷,其中有一條就是這個 GC唐责。

在老式的 C/C++ 程序中,程序員定義了一個變量瘾带,就是在內存中開辟了一段相應的空間來存值妒蔚。由于內存是有限的,所以當程序不再需要使用該變量的時候月弛,就需要銷毀該對象并釋放其所占用的內存資源,好重新利用這段空間科盛。在 C/C++ 中帽衙,釋放無用變量內存空間的事情需要由程序員來處理,就是說當程序員認為變量沒用了贞绵,就手動釋放其占用的內存厉萝。但是這樣非常繁瑣,如果有所遺漏榨崩,就可能造成資源浪費甚至內存泄露谴垫。當軟件系統(tǒng)比較復雜,變量多的時候程序員往往就忘記釋放內存或者在不該釋放的時候釋放內存了母蛛。

有了 GC翩剪,程序員就不再需要手動的去控制內存的釋放。當 JVM 或 .NET CLR 發(fā)覺內存資源緊張的時候彩郊,就會自動地去清理無用對象(沒有被引用到的對象)所占用的內存空間(這里的說法略顯粗略前弯,事實上何時清理內存是個復雜的策略)蚪缀。如果需要,可以在程序中顯式地使用System.gc()來強制進行一次立即的內存清理恕出。Java 提供的 GC 功能可以自動監(jiān)測對象是否超過了作用域询枚,從而達到自動回收內存的目的,Java 的 GC 會自動進行管理浙巫,調用方法:System.gc()或者Runtime.getRuntime().gc()金蜀。

二、堆內存劃分及GC類型

1??堆內存根據(jù)對象存活的生命周期被劃分為兩塊的畴,一塊是新生代(Young Generation)渊抄,另一塊是老年代(Tenured Generation),大概比例是:新生代:老年代 == 1:2苗傅。新生代又分為 Eden 和 Survivor抒线,二者空間大小比例默認為Eden:Survivor == 8:2。幸存區(qū)又分為S0和S1渣慕,二者空間大小是一模一樣的嘶炭。在堆區(qū)之外還有一個元空間(MetaqSpace)。JDK8 HotSpot 中移除了永久代(Permanet Generation)逊桦,使用 MetaSpace 來代替眨猎,MetaSpace 是使用本地內存來存儲類元數(shù)據(jù)信息。內存容量取決于操作系統(tǒng)虛擬內存大小强经,通過參數(shù) -MaxMetaspaceSize 來限制 MetaSpace 的大小睡陪。

2??“對象進入到老年代”的四種情況

  1. Minor GC/Young GC 時,To Survivor 區(qū)不足以存放存活的對象匿情,對象會直接進入到老年代兰迫。
  2. 經(jīng)過多次 Minor GC/Young GC 后,如果存活對象的年齡達到了設定閾值炬称,則會晉升到老年代中汁果。
  3. 動態(tài)年齡判定規(guī)則,To Survivor 區(qū)中相同年齡的對象玲躯,如果其大小之和占到了 To Survivor 區(qū)一半以上的空間据德,那么大于此年齡的對象會直接進入老年代,而不需要達到默認的分代年齡跷车。
  4. 大對象:由-XX:PretenureSizeThreshold啟動參數(shù)控制棘利,若對象大小大于此值,就會繞過新生代朽缴,直接在老年代中分配善玫。HotSpot 虛擬機提供該參數(shù),指定大于其值的對象直接在老年代分配的意義在于密强,避免大對象在 Eden 區(qū)及兩個 Survivor 區(qū)之間來回復制蝌焚,產(chǎn)生大量的內存復制操作裹唆。

3??GC類型

  1. 部分收集(Partial GC):指收集的目標不是整個 Java 堆的垃圾收集。其中又分為:

①新生代收集(Minor GC/Young GC):【一般采用復制-回收算法】
指目標只是新生代的垃圾收集只洒,主要是由于 Eden 不夠分配了许帐。這個區(qū),大多數(shù) Java 對象都是朝生夕滅毕谴,所以 Minor GC 是非常頻繁的成畦,回收速度也比較快。

②老年代收集(Major GC/Old GC):【標記-清除算法】
指目標只是老年代的垃圾收集涝开。目前只有 CMS 收集器會有單獨收集老年代的行為循帐。另外請注意“Major GC”這個說法現(xiàn)在有點混淆,在不同資料上常有不同所指舀武, 讀者需按上下文區(qū)分到底是指老年代的收集還是整堆收集拄养。

③混合收集(Mixed GC):指目標是收集整個新生代以及部分老年代的垃圾收集。目前只有 G1 收集器會有這種行為银舱。

  1. 整堆收集(Full GC):收集整個 Java 堆和方法區(qū)的垃圾收集瘪匿。

因為 Full GC 要對整個堆進行回收,包括 Young寻馏、Tenured 和 MetaqSpace棋弥。所以比 Partial GC 要慢,因此應該盡可能減少 Full GC 的次數(shù)诚欠。在對 JVM 調優(yōu)的過程中顽染,很大一部分工作就是對于 Full GC 的調節(jié)。

三轰绵、堆內存垃圾回收過程

在新生代中粉寞,由于對象生存期短,每次垃圾回收時都有大量的對象需要被回收左腔,這時采用復制算法唧垦。老年代里的對象存活率較高,每次垃圾回收時只有少量對象需要被回收翔悠,沒有額外的空間進行分配擔保,這時采用標記-整理或者標記-清除算法野芒⌒畛睿可以根據(jù)不同代的特點采取最適合的收集算法。

分代收集算法

1??Minor GC/Young GC 的回收過程

  1. 大多數(shù)情況下狞悲,對象直接在年輕代中的 Eden 區(qū)進行分配撮抓,如果 Eden 區(qū)域沒有足夠的空間,那么就會觸發(fā) Minor GC摇锋,處理的區(qū)域只有新生代丹拯。因為大部分對象在短時間內都可以回收掉站超,因此 Minor GC/Young GC 后只有極少數(shù)的對象能存活下來,而被移動到S0區(qū)(采用的是復制算法)乖酬,并且對象的年齡設為 1死相。

  2. 當觸發(fā)下一次 Minor GC/Young GC 時,會將 Eden 區(qū)和S0區(qū)的存活對象移動到S1區(qū)咬像,同時清空 Eden 區(qū)和S0區(qū)算撮。當再次觸發(fā) Minor GC/Young GC 時,這時候處理的區(qū)域就變成了 Eden 區(qū)和S1區(qū)(即S0和S1進行角色交換)县昂。如此反復肮柜,對象在 Survivor 區(qū)中每“熬過”一次 Minor GC/Young GC,年齡就增加 1 歲倒彰,當它的年齡增加到一定程度(默認為 15 歲)审洞,就將會被晉升到老年代中。對象晉升老年代的閾值待讳,可以通過參數(shù)-XX:MaxTenuringThreshold 設置芒澜。

2??Full GC 的觸發(fā)時機

當晉升到老年代的對象大于了老年代的剩余空間時,就會觸發(fā) Full GC耙箍。除此之外撰糠,還有以下四種情況也會觸發(fā):

  1. 老年代的內存使用率達到了一定閾值(可通過參數(shù)調整),直接觸發(fā) Full GC辩昆。

  2. 空間分配擔保
    在發(fā)生 Minor GC/Young GC 之前阅酪,虛擬機必須先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對象總空間。

①如果大于汁针,那這一次 Minor GC/Young GC 可以確保是安全的相味。
②如果小于,則虛擬機會先查看- XX:HandlePromotionFailure的設置值是否允許擔保失敗(Handle Promotion Failure):

  • 如果允許镜悉,那會繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對象的平均大小喘沿,如果大于,將嘗試進行一次 Minor GC/Young GC猾骡,盡管這次 Minor GC/Young GC 是有風險的瑞躺;
  • 如果小于,或者-XX: HandlePromotionFailure設置不允許冒險兴想,那這時就要改為進行一次 Full GC幢哨。
  1. Metaspace(元空間)達到-XX:MetaspaceSize【元空間初始值,以字節(jié)為單位】的指定值時嫂便,也會觸發(fā) Full GC捞镰。同時收集器會對該值進行調整:如果釋放了大量的空間,就適當降低該值;如果釋放 了很少的空間岸售,那么在不超過-XX:MaxMetaspaceSize【元空間最大值践樱,默認是-1,即不限制凸丸,或者說只受限于本地內存大小拷邢。】(如果設置了的話)的情況下甲雅,適當提高該值解孙。

  2. System.gc()或者Runtime.gc()被顯式調用時,觸發(fā) Full GC抛人。

四弛姜、GC只回收堆內存和方法區(qū)內的對象

JVM 內存區(qū)域中程序計數(shù)器、虛擬機棧妖枚、本地方法棧三個區(qū)域隨線程而生廷臼、隨線程而滅。因此這三個區(qū)域的內存分配和回收都具備確定性绝页,就不需要過多考慮回收的問題荠商,因為方法結束或者線程結束時,內存自然就跟隨著回收了续誉。而 Java 堆區(qū)和方法區(qū)則不一樣莱没,這部分內存的分配和回收是動態(tài)的,正是垃圾收集器所需關注的部分酷鸦。

垃圾收集器在對堆區(qū)和方法區(qū)進行回收前饰躲,首先要確定這些區(qū)域的對象哪些可以被回收,哪些暫時還不能回收臼隔,這就要用到判斷對象是否存活的算法:
①引用計數(shù)算法
②可達性分析算法
③對象死亡(被回收)前的最后一次掙扎
④方法區(qū)如何判斷是否需要回收

1??引用計數(shù)算法

引用計數(shù)是垃圾收集器中的早期策略嘹裂。在這種方法中,堆中每個對象實例都有一個引用計數(shù)摔握。當一個對象被創(chuàng)建時寄狼,就將該對象實例分配給一個變量,該變量計數(shù)設置為 1氨淌。當任何其它變量被賦值為這個對象的引用時泊愧,計數(shù) +1(a = b,則 b 引用的對象實例的計數(shù)器 +1)盛正,但當一個對象實例的某個引用超過了生命周期或者被設置為一個新值時删咱,對象實例的引用計數(shù)器 -1。任何引用計數(shù)器為 0 的對象實例可以被當作垃圾收集蛮艰。當一個對象實例被垃圾收集時腋腮,它引用的任何對象實例的引用計數(shù)器 -1。

優(yōu)點:引用計數(shù)收集器可以很快的執(zhí)行壤蚜,交織在程序運行中即寡。對程序需要不被長時間打斷的實時環(huán)境比較有利。
缺點:無法檢測出循環(huán)引用袜刷。如父對象有一個對子對象的引用聪富,子對象反過來引用父對象。這樣著蟹,它們的引用計數(shù)永遠不可能為 0墩蔓。

public class ReferenceFindTest {
  public static void main(String[] args) {
     MyObject object1 = new MyObject();
     MyObject object2 = new MyObject();

     object1.object = object2;
     object2.object = object1;

     object1 = null;
     object2 = null;
  }
}

這段代碼是用來驗證引用計數(shù)算法不能檢測出循環(huán)引用。最后兩句將 object1 和 object2 賦值為 null萧豆,也就是說 object1 和 object2 指向的對象已經(jīng)不可能再被訪問奸披,但是由于它們互相引用對方,導致它們的引用計數(shù)器都不為 0涮雷,那么垃圾收集器就永遠不會回收它們阵面。

2??可達性分析算法

該算法是從離散數(shù)學中的圖論引入的,程序把所有的引用關系看作一張圖洪鸭,從一個節(jié)點 GC ROOT 開始样刷,尋找對應的引用節(jié)點,找到這個節(jié)點以后览爵,繼續(xù)尋找這個節(jié)點的引用節(jié)點置鼻,當所有的引用節(jié)點尋找完畢之后,剩余的節(jié)點則被認為是沒有被引用到的節(jié)點蜓竹,即無用的節(jié)點箕母,無用的節(jié)點將會被判定為是可回收的對象。
引用節(jié)點圖論

在 Java 中梅肤,可作為 GC Roots 的對象包括下面幾種:
①虛擬機棧中引用的對象(棧幀中的本地變量表)司蔬;
②方法區(qū)中類靜態(tài)屬性引用的對象;
③方法區(qū)中常量引用的對象姨蝴;
④本地方法棧中 JNI(Native方法) 引用的對象俊啼。

無論是通過引用計數(shù)算法判斷對象的引用數(shù)量,還是通過可達性分析算法判斷對象的引用鏈是否可達左医,判定對象是否存活都與“引用”有關授帕。在 Java 中,將引用又分為強引用浮梢、軟引用跛十、弱引用、虛引用四種秕硝,這四種引用強度依次逐漸減弱芥映。無論引用計數(shù)算法還是可達性分析算法都是基于強引用而言的。

3??對象死亡(被回收)前的最后一次掙扎

即使在可達性分析算法中不可達的對象,也并非是“非死不可”奈偏,這時候它們暫時處于“緩刑”階段坞嘀,要真正宣告一個對象死亡,至少要經(jīng)歷兩次標記過程惊来。

第①次標記:如果對象在進行可達性分析后發(fā)現(xiàn)沒有與 GC Roots 相連接的引用鏈丽涩,那它將會被第一次標記;
第②次標記:第一次標記后接著會進行一次篩選裁蚁,篩選的條件是此對象是否有必要執(zhí)行 finalize()矢渊。在 finalize() 中沒有重新與引用鏈建立關聯(lián)關系的,將被進行第二次標記枉证。

第二次標記成功的對象將真的會被回收矮男,如果對象在 finalize() 中重新與引用鏈建立了關聯(lián)關系,那么將會逃離本次回收室谚,繼續(xù)存活昂灵。

4??方法區(qū)如何判斷是否需要回收

方法區(qū)存儲內容是否需要回收的判斷不同于上。方法區(qū)主要回收的內容有:廢棄常量和無用的類舞萄。對于廢棄常量也可通過引用的可達性來判斷眨补,但是對于無用的類則需要同時滿足下面三個條件:
①該類所有的實例都已經(jīng)被回收,也就是 Java 堆中不存在該類的任何實例倒脓;
②加載該類的 ClassLoader 已經(jīng)被回收撑螺;
③該類對應的java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法崎弃。

五甘晤、常用的垃圾收集算法

1??標記-清除算法

標記-清除算法采用從根集合(GC Roots)進行掃描,對存活的對象進行標記饲做,標記完畢后线婚,再掃描整個空間中未被標記的對象,進行回收盆均,如圖塞弊。該算法不涉及對象的移動,只處理不存活的對象泪姨,在存活對象比較多的情況下相對高效游沿。但是會有兩個主要問題:①效率不高,標記和清除的效率都很低肮砾;②會產(chǎn)生大量不連續(xù)的內存碎片诀黍,導致以后程序在分配較大的對象時,由于沒有充足的連續(xù)內存而提前觸發(fā)一次 GC 動作仗处。
標記-清除算法

2??復制算法【參見第三項】

復制算法的提出是為了克服句柄的開銷和解決內存碎片的問題眯勾。它開始時把堆分成一個對象面和多個空閑面枣宫,程序從對象面為對象分配空間,當對象滿了吃环,基于復制算法的垃圾收集就從根集合(GC Roots)中掃描還活動的對象镶柱,并將每個活動對象復制到空閑面(使得活動對象所占的內存之間沒有空閑洞),這樣空閑面變成了對象面模叙,原來的對象面變成了空閑面,程序會在新的對象面中分配內存鞋屈。然后一次性清除完第一塊內存范咨,再將第二塊上的對象復制到第一塊。但是這種方式厂庇,內存的代價太高渠啊,每次基本上都要浪費一半的內存。于是將該算法進行了改進权旷,內存區(qū)域不再是按照 1:1 去劃分替蛉,而是將內存劃分為 8:1:1 三部分,較大那份內存叫 Eden 區(qū)拄氯,其余是兩塊較小的內存區(qū)叫 Survior 區(qū)躲查。每次都會優(yōu)先使用 Eden 區(qū),若 Eden 區(qū)滿译柏,就將對象復制到第二塊內存區(qū)上镣煮,然后清除 Eden 區(qū),如果此時存活的對象太多鄙麦,以至于 Survivor 不夠時典唇,會將這些對象通過分配擔保機制復制到老年代中。
復制算法

3??標記-整理算法

該算法采用標記-清除算法一樣的方式進行對象的標記胯府,但在清除時不同介衔,在回收不存活的對象占用的空間后,會將所有的存活對象往左端空閑空間移動骂因,并更新對應的指針炎咖。該算法是在標記-清除算法的基礎上,又進行了對象的移動寒波,因此成本更高塘装,但是卻解決了內存碎片的問題。當對象存活率較高時影所,也解決了復制算法的效率問題蹦肴。具體流程如圖:
標記-整理算法

六、垃圾收集器

如圖是 HotSpot 虛擬機包含的所有收集器:

新生代收集器:Serial猴娩、ParNew阴幌、Parallel Scavenge
老年代收集器:CMS勺阐、Serial Old、Parallel Old
新生代和老年代的收集器之間進行連線矛双,說明它們之間可以搭配使用渊抽。

七、新生代垃圾收集器

1??Serial 垃圾收集器---串行收集器【復制算法】
Serial 收集器是最基本的议忽、發(fā)展歷史最悠久的收集器懒闷。

特點:
串行收集器是指使用單線程進行垃圾回收的收集器。每次回收時栈幸,串行收集器只有一個工作線程愤估,暫停其他線程。對于并行能力較弱的單 CPU 計算機來說速址,串行收集器的專注性和獨占性往往有更好的性能表現(xiàn)玩焰。它存在 Stop The World 問題,即垃圾回收時芍锚,要停止程序的運行昔园。使用-XX:+UseSerialGC參數(shù)可以設置新生代使用這個串行收集器。

2??默認ParNew 垃圾收集器---并行收集器【復制算法】
ParNew 其實就是 Serial 的多線程版本并炮,除了使用多線程之外默刚,其余參數(shù)和 Serial 一模一樣。

特點:
ParNew 默認開啟的線程數(shù)與 CPU 數(shù)量相同逃魄,在 CPU 核數(shù)很多的機器上羡棵,可以通過參數(shù)-XX:ParallelGCThreads來設置線程數(shù)。它是目前新生代首選的垃圾收集器嗅钻,因為除了 ParNew 之外皂冰,它是唯一一個能與老年代 CMS 配合工作的。它同樣存在 Stop The World 問題养篓。使用-XX:+UseParNewGC參數(shù)可以設置新生代使用這個并行收集器秃流。

3??Parallel Scavenge 收集器
Parallel Scavenge 使用復制算法回收垃圾,也是多線程的柳弄。

特點:
就是非常關注系統(tǒng)的吞吐量舶胀,吞吐量=代碼運行時間/(代碼運行時間+垃圾收集時間)

-XX:MaxGCPauseMillis:設置最大垃圾收集停頓時間,可用把虛擬機在GC停頓的時間控制在 MaxGCPauseMillis 范圍內碧注,如果希望減少 GC 停頓時間可以將 MaxGCPauseMillis 設置的很小嚣伐,但是會導致 GC 頻繁,從而增加了 GC 的總時間萍丐,降低了吞吐量轩端。所以需要根據(jù)實際情況設置該值。

-Xx:GCTimeRatio:設置吞吐量大小逝变,它是一個0到100之間的整數(shù)基茵,默認情況下他的取值是99奋构,那么系統(tǒng)將花費不超過1/(1+n)的時間用于垃圾回收,也就是1/(1+99)=1%的時間拱层。

另外還可以指定-XX:+UseAdaptiveSizePolicy打開自適應模式弥臼,在這種模式下,新生代的大小根灯、eden径缅、from/to的比例,以及晉升老年代的對象年齡參數(shù)會被自動調整烙肺,以達到在堆大小纳猪、吞吐量和停頓時間之間的平衡點。

使用-XX:+UseParallelGC參數(shù)可以設置新生代使用這個并行收集器茬高。

八、老年代垃圾收集器

1??SerialOld 垃圾收集器---串行收集器【標記 - 整理算法】
SerialOld 是 Serial 收集器的老年代收集器版本假抄,它同樣是一個單線程收集器怎栽。

用途:
一個是在JDK1.5及之前的版本中與 Parallel Scavenge 收集器搭配使用。另一個就是作為 CMS 收集器的后備預案宿饱,如果 CMS 出現(xiàn) Concurrent Mode Failure熏瞄,則 SerialOld 將作為后備收集器。

2??Parallel Old 垃圾收集器---并行收集器【標記壓縮算法】
老年代 Parallel Old 垃圾收集器也是一種多線程的收集器谬以,和新生代的 Parallel Scavenge 收集器一樣强饮,也是一種關注吞吐量的收集器。使用-XX:+UseParallelOldGc設置老年代使用該收集器为黎。使用-XX:+ParallelGCThreads設置垃圾收集時的線程數(shù)量邮丰。

3??CMS 垃圾收集器【標記清除法】
CMS 全稱 Concurrent Mark Sweep 意為并發(fā)標記清除。主要關注系統(tǒng)停頓時間铭乾。使用-XX:+UseConcMarkSweepGC設置老年代使用該收集器剪廉。使用-XX:ConcGCThreads設置垃圾收集時的線程數(shù)量。

特點:
CMS 并不是獨占的收集器炕檩,也就說 CMS 回收的過程中斗蒋,應用程序仍然在不停的工作,又會有新的垃圾不斷的產(chǎn)生笛质,所以在使用 CMS 的過程中應該確保應用程序的內存足夠可用泉沾。

CMS 不會等到應用程序飽和的時候才去回收垃圾,而是在某一閥值的時候開始回收妇押,回收閥值可用指定的參數(shù)進行配置:-XX:CMSInitiatingoccupancyFraction來指定跷究,默認為 68,也就是說當老年代的空間使用率達到 68% 的時候敲霍,會執(zhí)行 CMS揭朝。

如果內存使用率增長的很快队贱,在 CMS 執(zhí)行的過程中,已經(jīng)出現(xiàn)了內存不足的情況潭袱,此時 CMS 回收就會失敗柱嫌,虛擬機將啟動老年代串行收集器 SerialOld GC 進行垃圾回收,這會導致應用程序中斷屯换,直到垃圾回收完成后才會正常工作编丘。這個過程 GC 的停頓時間可能較長,所以-XX:CMSInitiatingoccupancyFraction的設置要根據(jù)實際的情況彤悔。

標記清除法有個缺點就是存在內存碎片的問題嘉抓,那么 CMS 有個參數(shù)設置-XX:+UseCMSCompactAtFullCollecion可以使 CMS 回收完成之后進行一次碎片整理。使用-XX:CMSFullGCsBeforeCompaction設置進行多少次 CMS 回收之后晕窑,對內存進行一次壓縮抑片。

在 CMS GC 過程中,由于老年代剩余空間無法存放需要分配的對象杨赤,會產(chǎn)生concurrent-mode-failure敞斋。

九、堆內存常見參數(shù)配置

十疾牲、TLAB 內存

TLAB 全稱是 Thread Local Allocation Buffer 即線程本地分配緩存植捎,從名字上看是一個線程專用的內存分配區(qū)域,是為了加速對象分配而生的阳柔。每一個線程都會產(chǎn)生一個 TLAB焰枢,該線程獨享的工作區(qū)域,JVM 使用這種 TLAB 區(qū)來避免多線程沖突問題舌剂,提高了對象分配的效率济锄。TLAB 空間一般不會太大,當大對象無法在 TLAB 分配時霍转,則會直接分配到堆上拟淮。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市谴忧,隨后出現(xiàn)的幾起案子很泊,更是在濱河造成了極大的恐慌,老刑警劉巖沾谓,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件委造,死亡現(xiàn)場離奇詭異,居然都是意外死亡均驶,警方通過查閱死者的電腦和手機昏兆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來妇穴,“玉大人爬虱,你說我怎么就攤上這事隶债。” “怎么了跑筝?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵死讹,是天一觀的道長。 經(jīng)常有香客問我曲梗,道長赞警,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任虏两,我火速辦了婚禮愧旦,結果婚禮上,老公的妹妹穿的比我還像新娘定罢。我一直安慰自己笤虫,他們只是感情好,可當我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布祖凫。 她就那樣靜靜地躺著琼蚯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蝙场。 梳的紋絲不亂的頭發(fā)上凌停,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天粱年,我揣著相機與錄音售滤,去河邊找鬼。 笑死台诗,一個胖子當著我的面吹牛完箩,可吹牛的內容都是我干的。 我是一名探鬼主播拉队,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼弊知,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了粱快?” 一聲冷哼從身側響起秩彤,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎事哭,沒想到半個月后漫雷,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡鳍咱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年降盹,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谤辜。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡蓄坏,死狀恐怖价捧,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情涡戳,我是刑警寧澤结蟋,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站妹蔽,受9級特大地震影響椎眯,放射性物質發(fā)生泄漏。R本人自食惡果不足惜胳岂,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一编整、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧乳丰,春花似錦掌测、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至什燕,卻和暖如春粘勒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背屎即。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工庙睡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人技俐。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓乘陪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親雕擂。 傳聞我的和親對象是個殘疾皇子啡邑,可洞房花燭夜當晚...
    茶點故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內容