24. java虛擬機(jī)總結(jié)-垃圾回收器 (七)

關(guān)于 JVM 垃圾回收機(jī)制面試中主要涉及這三個(gè)考題:

    JVM 中有哪些垃圾回收算法?它們各自有什么優(yōu)劣?

    CMS 垃圾回收器是怎么工作的?有哪些階段?

    服務(wù)卡頓的元兇到底是誰(shuí)?

JVM 是有專門的線程在做這件事情。當(dāng)我們的內(nèi)存空間達(dá)到一定條件時(shí),會(huì)自動(dòng)觸發(fā)。這個(gè)過程就叫作 GC,負(fù)責(zé) GC 的組件,就叫作垃圾回收器。

JVM 規(guī)范并沒有規(guī)定垃圾回收器怎么實(shí)現(xiàn),它只需要保證不要把正在使用的對(duì)象給回收掉就可以。在現(xiàn)在的服務(wù)器環(huán)境中,經(jīng)常被使用的垃圾回收器有`CMS 和 G1,但 JVM 還有其他幾個(gè)常見的垃圾回收器。

按照語(yǔ)義上的意思,垃圾回收,首先就需要找到這些垃圾,然后回收掉。但是 GC 過程正好相反,它是先找到活躍的對(duì)象,然后把其他不活躍的對(duì)象判定為垃圾,然后刪除。所以垃圾回收只與活躍的對(duì)象有關(guān),和堆的大小無關(guān)。

首先介紹幾種非常重要的回收算法

標(biāo)記(Mark)

根據(jù) GC Roots 遍歷所有的可達(dá)對(duì)象,這個(gè)過程,就叫作標(biāo)記。


標(biāo)記.png

如圖所示,圓圈代表的是對(duì)象。綠色的代表 GC Roots,紅色的代表可以追溯到的對(duì)象。可以看到標(biāo)記之后,仍然有多個(gè)灰色的圓圈,它們都是被回收的對(duì)象。

清除(Sweep)

清除階段就是把未被標(biāo)記的對(duì)象回收掉。

清除.png

這種簡(jiǎn)單的清除方式,有一個(gè)明顯的弊端,那就是內(nèi)存碎片問題。比如我申請(qǐng)了 1k、2k、3k、4k、5k 的內(nèi)存。
申請(qǐng)內(nèi)存.jpg

由于某種原因 ,2k 和 4k 的內(nèi)存,我不再使用,就需要交給垃圾回收器回收。
清理內(nèi)存.jpg

這個(gè)時(shí)候,我應(yīng)該有足足 6k 的空閑空間。接下來,我打算申請(qǐng)另外一個(gè) 5k 的空間,結(jié)果系統(tǒng)告訴我內(nèi)存不足了。系統(tǒng)運(yùn)行時(shí)間越長(zhǎng),這種碎片就越多。

在很久之前使用 Windows 系統(tǒng)時(shí),有一個(gè)非常有用的功能,就是內(nèi)存整理和磁盤整理,運(yùn)行之后有可能會(huì)顯著提高系統(tǒng)性能。這個(gè)出發(fā)點(diǎn)是一樣的。

復(fù)制(Copy)

解決碎片問題只有進(jìn)行內(nèi)存整理。比較好的思路可以完成這個(gè)整理過程,就是提供一個(gè)對(duì)等的內(nèi)存空間,將存活的對(duì)象復(fù)制過去,然后清除原內(nèi)存空間。復(fù)制算法是非常有效的。比如:HashMap 的擴(kuò)容也是使用同樣的思路

整個(gè)過程如圖所示:

復(fù)制.jpg

它的弊端也非常明顯。它浪費(fèi)了幾乎一半的內(nèi)存空間來做這個(gè)事情。

整理(Compact)

不用分配一個(gè)對(duì)等的額外空間,也是可以完成內(nèi)存的整理工作。
你可以把內(nèi)存想象成一個(gè)非常大的數(shù)組,根據(jù)隨機(jī)的 index 刪除了一些數(shù)據(jù)。那么對(duì)整個(gè)數(shù)組的清理,其實(shí)是不需要另外一個(gè)數(shù)組來進(jìn)行支持的,繼續(xù)使用這個(gè)數(shù)組就可以實(shí)現(xiàn)。
它的主要思路,就是移動(dòng)所有存活的對(duì)象,且按照內(nèi)存地址順序依次排列,然后將末端內(nèi)存地址以后的內(nèi)存全部回收。

整理.png

從效率上來說,一般整理算法是要低于復(fù)制算法的。

這幾種算法的特點(diǎn)

    復(fù)制算法(Copy)復(fù)制算法是所有算法里面效率最高的,缺點(diǎn)是會(huì)造成一定的空間浪費(fèi)。
    標(biāo)記-清除(Mark-Sweep)效率一般,缺點(diǎn)是會(huì)造成內(nèi)存碎片問題。
    標(biāo)記-整理(Mark-Compact)效率比前兩者要差,但沒有空間浪費(fèi),也消除了內(nèi)存碎片問題。

分代

JVM 是計(jì)算節(jié)點(diǎn),而不是存儲(chǔ)節(jié)點(diǎn)。最理想的情況,就是對(duì)象在用完之后,它的生命周期立馬就結(jié)束了。而那些被頻繁訪問的資源,我們希望它能夠常駐在內(nèi)存里。

研究表明,大部分對(duì)象,可以分為兩類:

    大部分對(duì)象的生命周期都很短;
    其他對(duì)象則很可能會(huì)存活很長(zhǎng)時(shí)間。

大部分死的快,其他的活的長(zhǎng)。這個(gè)假設(shè)我們稱之為弱代假設(shè)(weak generational hypothesis)。


分代.jpg

從圖中可以看到,大部分對(duì)象是朝生夕滅的,其他的則活的很久。

現(xiàn)在的垃圾回收器,都會(huì)在物理上或者邏輯上,把這兩類對(duì)象進(jìn)行區(qū)分。我們把死的快的對(duì)象所占的區(qū)域,叫作年輕代(Young generation)。把其他活的長(zhǎng)的對(duì)象所占的區(qū)域,叫作老年代(Old generation)。


年輕代老年代.jpg
年輕代

年輕代使用的垃圾回收算法是復(fù)制算法。因?yàn)槟贻p代發(fā)生 GC 后,只會(huì)有非常少的對(duì)象存活,復(fù)制這部分對(duì)象是非常高效的。

我們前面也了解到復(fù)制算法會(huì)造成一定的空間浪費(fèi),所以年輕代中間也會(huì)分很多區(qū)域。


年輕代分區(qū).jpg

如圖所示,年輕代分為:一個(gè)伊甸園空間(Eden ),兩個(gè)幸存者空間(Survivor )。

當(dāng)年輕代中的 Eden 區(qū)分配滿的時(shí)候,就會(huì)觸發(fā)年輕代的 GC(Minor GC)。具體過程如下:

在 Eden 區(qū)執(zhí)行了第一次 GC 之后,存活的對(duì)象會(huì)被移動(dòng)到其中一個(gè) Survivor 分區(qū)(以下簡(jiǎn)稱from);

Eden 區(qū)再次 GC,這時(shí)會(huì)采用復(fù)制算法,將 Eden 和 from 區(qū)一起清理。存活的對(duì)象會(huì)被復(fù)制到 to 區(qū);接下來,只需要清空 from 區(qū)就可以了。

所以在這個(gè)過程中,總會(huì)有一個(gè) Survivor 分區(qū)是空置的。Eden、from、to 的默認(rèn)比例是 8:1:1,所以只會(huì)造成 10% 的空間浪費(fèi)。這個(gè)比例,是由參數(shù) -XX:SurvivorRatio進(jìn)行配置的(默認(rèn)為 8)。

一般情況下,我們只需要了解到這一層面就 OK 了。但是在平常的面試中,還有一個(gè)點(diǎn)會(huì)經(jīng)常提到,雖然頻率不太高,它就是 TLAB,我們?cè)谶@里也簡(jiǎn)單介紹一下。
這個(gè)道理和 Java 語(yǔ)言中的 ThreadLocal 類似,避免了對(duì)公共區(qū)的操作,以及一些鎖競(jìng)爭(zhēng)。
TLAB 的全稱是 Thread Local Allocation Buffer,JVM 默認(rèn)給每個(gè)線程開辟一個(gè) buffer 區(qū)域,用來加速對(duì)象分配。這個(gè) buffer 就放在 Eden 區(qū)中。

對(duì)象的分配優(yōu)先在 TLAB上 分配,但 TLAB 通常都很小,所以對(duì)象相對(duì)比較大的時(shí)候,會(huì)在 Eden 區(qū)的共享區(qū)域進(jìn)行分配。

TLAB 是一種優(yōu)化技術(shù),類似的優(yōu)化還有對(duì)象的棧上分配(這可以引出逃逸分析的話題,默認(rèn)開啟)。這屬于非常細(xì)節(jié)的優(yōu)化,不做過多介紹,但偶爾面試也會(huì)被問到。

Java對(duì)象分配流程

截屏2021-07-21 上午7.58.20.png

棧上分配

將線程私有的對(duì)象打散分配在棧上,優(yōu)點(diǎn)是可以在函數(shù)調(diào)用結(jié)束后自行銷毀對(duì)象,不需要垃圾回收器的介入,并且棧上分配速度快,提高系統(tǒng)性能,但缺點(diǎn)是棧空間小,對(duì)于大對(duì)象無法實(shí)現(xiàn)棧上分配。棧上分配的前提條件是開啟了逃逸分析(-XX:+DoEscapeAnalysis):判斷對(duì)象的作用域是否超出函數(shù)體,只有作用域沒有超出函數(shù)體的對(duì)象才能棧上分配。如下,user的作用域超出了函數(shù)setUser的范圍,是逃逸對(duì)象,不能進(jìn)行棧上分配。棧上分配還有一個(gè)前提是開啟標(biāo)量替換 (-XX:-EliminateAllocations),逃逸分析和標(biāo)量替換都是jvm默認(rèn)開啟的

標(biāo)量替換:簡(jiǎn)單地說,就是用標(biāo)量替換聚合量。標(biāo)量是指不可分割的量,如java中基本數(shù)據(jù)類型和reference類型,相對(duì)的一個(gè)數(shù)據(jù)可以繼續(xù)分解,稱為聚合量;如果把一個(gè)對(duì)象拆散,將其成員變量恢復(fù)到基本類型來訪問就叫做標(biāo)量替換;如果逃逸分析發(fā)現(xiàn)一個(gè)對(duì)象不會(huì)被外部訪問,并且該對(duì)象可以被拆散,那么經(jīng)過優(yōu)化之后,并不直接生成該對(duì)象,而是在棧上創(chuàng)建若干個(gè)成員變量;

private User user;
public void setUser(){
    user = new User();
    user.setId(1);
    user.setName("blueStarWei");
}

TLAB 分配

Thread Local Allocation Buffer, 線程本地分配緩存。一塊線程專用的內(nèi)存分配區(qū)域。TLAB占用的是eden區(qū)的空間(注意是堆上)。在TLAB啟用的情況下(默認(rèn)開啟),JVM會(huì)為每一個(gè)線程分配一塊TLAB區(qū)域。

優(yōu)點(diǎn)是可以加速對(duì)象的分配。因?yàn)門LAB是線程專有區(qū)域,會(huì)減少線程同步操作,使分配的效率提高。考慮到對(duì)象分配幾乎是Java中最常用的操作,因此JVM使用了TLAB這樣的線程專有區(qū)域來避免多線程沖突,提高對(duì)象分配的效率。

老年代

老年代一般使用“標(biāo)記-清除”、“標(biāo)記-整理”算法,因?yàn)槔夏甏膶?duì)象存活率一般是比較高的,空間又比較大,拷貝起來并不劃算,還不如采取就地收集的方式。

那么,對(duì)象是怎么進(jìn)入老年代的呢?有多種途徑。

1.提升(Promotion)

如果對(duì)象夠老,會(huì)通過“提升”進(jìn)入老年代。
關(guān)于對(duì)象老不老,是通過它的年齡(age)來判斷的。每當(dāng)發(fā)生一次 Minor GC,存活下來的對(duì)象年齡都會(huì)加 1。直到達(dá)到一定的閾值,就會(huì)把這些“老頑固”給提升到老年代
這些對(duì)象如果變的不可達(dá),直到老年代發(fā)生 GC 的時(shí)候,才會(huì)被清理掉。
這個(gè)閾值,可以通過參數(shù) ‐XX:+MaxTenuringThreshold 進(jìn)行配置,最大值是 15,因?yàn)樗怯?4bit 存儲(chǔ)的(所以網(wǎng)絡(luò)上那些要把這個(gè)值調(diào)的很大的文章,是沒有什么根據(jù)的)。

2.分配擔(dān)保

看一下年輕代的圖,每次存活的對(duì)象,都會(huì)放入其中一個(gè)幸存區(qū),這個(gè)區(qū)域默認(rèn)的比例是 10%。但是我們無法保證每次存活的對(duì)象都小于 10%,當(dāng) Survivor 空間不夠,就需要依賴其他內(nèi)存(指老年代)進(jìn)行分配擔(dān)保。這個(gè)時(shí)候,對(duì)象也會(huì)直接在老年代上分配。

3.大對(duì)象直接在老年代分配

超出某個(gè)大小的對(duì)象將直接在老年代分配。這個(gè)值是通過參數(shù)-XX:PretenureSizeThreshold進(jìn)行配置的。默認(rèn)為 0,意思是全部首選 Eden 區(qū)進(jìn)行分配。

4.動(dòng)態(tài)對(duì)象年齡判定

有的垃圾回收算法,并不要求 age 必須達(dá)到 15 才能晉升到老年代,它會(huì)使用一些動(dòng)態(tài)的計(jì)算方法。比如,如果幸存區(qū)中相同年齡對(duì)象大小的和,大于幸存區(qū)的一半,大于或等于 age 的對(duì)象將會(huì)直接進(jìn)入老年代。

這些動(dòng)態(tài)判定一般不受外部控制,我們知道有這么回事就可以了。通過下圖可以看一下一個(gè)對(duì)象的分配邏輯。下圖中的SLAB改成TLAB


對(duì)象分配區(qū)域.jpg

卡片標(biāo)記(card marking)

你可以看到,對(duì)象的引用關(guān)系是一個(gè)巨大的網(wǎng)狀。有的對(duì)象可能在 Eden 區(qū),有的可能在老年代,那么這種跨代的引用是如何處理的呢?由于 Minor GC 是單獨(dú)發(fā)生的,如果一個(gè)老年代的對(duì)象引用了它,如何確保能夠讓年輕代的對(duì)象存活呢?

老年代是被分成眾多的卡頁(yè)(card page)的(一般數(shù)量是 2 的次冪)。

卡表(Card Table)就是用于標(biāo)記卡頁(yè)狀態(tài)的一個(gè)集合,每個(gè)卡表項(xiàng)對(duì)應(yīng)一個(gè)卡頁(yè)。

如果年輕代有對(duì)象分配,而且老年代有對(duì)象指向這個(gè)新對(duì)象, 那么這個(gè)老年代對(duì)象所對(duì)應(yīng)內(nèi)存的卡頁(yè),就會(huì)標(biāo)識(shí)為 dirty,卡表只需要非常小的存儲(chǔ)空間就可以保留這些狀態(tài)。

垃圾回收時(shí),就可以先讀這個(gè)卡表,進(jìn)行快速判斷。

HotSpot 垃圾回收器

接下來介紹 HotSpot 的幾個(gè)垃圾回收器,每種回收器都有各自的特點(diǎn)。我們?cè)谄匠5?GC 優(yōu)化時(shí),一定要搞清楚現(xiàn)在用的是哪種垃圾回收器。

在此之前,我們把上面的分代垃圾回收整理成一張大圖,在介紹下面的收集器時(shí),你可以對(duì)應(yīng)一下它們的位置。


分代垃圾回收.jpg

年輕代垃圾回收器

(1)Serial 垃圾收集器

處理 GC 的只有一條線程,并且在垃圾回收的過程中暫停一切用戶線程。
最簡(jiǎn)單的垃圾回收器,因?yàn)楹?jiǎn)單,所以高效,它通常用在客戶端應(yīng)用上。因?yàn)榭蛻舳藨?yīng)用不會(huì)頻繁創(chuàng)建很多對(duì)象,用戶也不會(huì)感覺出明顯的卡頓。相反,它使用的資源更少,也更輕量級(jí)。

(2)ParNew 垃圾收集器

ParNew 是 Serial 的多線程版本。由多條 GC 線程并行地進(jìn)行垃圾清理。清理過程依然要停止用戶線程。
ParNew 追求“低停頓時(shí)間”,與 Serial 唯一區(qū)別就是使用了多線程進(jìn)行垃圾收集,在多 CPU 環(huán)境下性能比 Serial 會(huì)有一定程度的提升;但線程切換需要額外的開銷,因此在單 CPU 環(huán)境中表現(xiàn)不如 Serial。

(3)Parallel Scavenge 垃圾收集器

另一個(gè)多線程版本的垃圾回收器。它與 ParNew 的主要區(qū)別是:
Parallel Scavenge:追求 CPU 吞吐量,能夠在較短時(shí)間內(nèi)完成指定任務(wù),適合沒有交互的后臺(tái)計(jì)算。弱交互強(qiáng)計(jì)算。
ParNew:追求降低用戶停頓時(shí)間,適合交互式應(yīng)用。強(qiáng)交互弱計(jì)算。

老年代垃圾收集器

(1)Serial Old 垃圾收集器

與年輕代的 Serial 垃圾收集器對(duì)應(yīng),都是單線程版本,同樣適合客戶端使用。
年輕代的 Serial,使用復(fù)制算法。
老年代的 Old Serial,使用標(biāo)記-整理算法。

(2)Parallel Old

Parallel Old 收集器是 Parallel Scavenge 的老年代版本,追求 CPU 吞吐量。

(3)CMS 垃圾收集器

CMS(Concurrent Mark Sweep)收集器是以獲取最短 GC 停頓時(shí)間為目標(biāo)的收集器,它在垃圾收集時(shí)使得用戶線程和 GC 線程能夠并發(fā)執(zhí)行,因此在垃圾收集過程中用戶也不會(huì)感到明顯的卡頓。我們會(huì)在后面的課時(shí)詳細(xì)介紹它。

長(zhǎng)期來看,CMS 垃圾回收器,是要被 G1 等垃圾回收器替換掉的。在 Java8 之后,使用它將會(huì)拋出一個(gè)警告。

Java HotSpot(TM) 64-Bit Server VM warning: Option UseConcMarkSweepGC was 
deprecated in version 9.0 and will likely be removed in a future release.
配置參數(shù)

除了上面幾個(gè)垃圾回收器,我們還有 G1、ZGC 等更加高級(jí)的垃圾回收器,它們都有專門的配置參數(shù)來使其生效。
通過 -XX:+PrintCommandLineFlags 參數(shù),可以查看當(dāng)前 Java 版本默認(rèn)使用的垃圾回收器。你可以看下我的系統(tǒng)中 Java13 默認(rèn)的收集器就是 G1。

java  -XX:+PrintCommandLineFlags  -version
-XX:G1ConcRefinementThreads=4 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=134217728 -XX:MaxHeapSize=2147483648 -XX:MinHeapSize=6815736 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC
java version "13.0.1" 2019-10-15
Java(TM) SE Runtime Environment (build 13.0.1+9)
Java HotSpot(TM) 64-Bit Server VM (build 13.0.1+9, mixed mode, sharing)

以下是一些配置參數(shù):

-XX:+UseSerialGC 年輕代和老年代都用串行收集器

-XX:+UseParNewGC 年輕代使用 ParNew,老年代使用 Serial Old

-XX:+UseParallelGC 年輕代使用 ParallerGC,老年代使用 Serial Old

-XX:+UseParallelOldGC 新生代和老年代都使用并行收集器

-XX:+UseConcMarkSweepGC,表示年輕代使用 ParNew,老年代的用 CMS

-XX:+UseG1GC 使用 G1垃圾回收器

-XX:+UseZGC 使用 ZGC 垃圾回收器

為了讓你有個(gè)更好的印象,請(qǐng)看下圖。它們的關(guān)系還是比較復(fù)雜的。尤其注意 -XX:+UseParNewGC 這個(gè)參數(shù),已經(jīng)在 Java9 中就被拋棄了。很多程序(比如 ES)會(huì)報(bào)這個(gè)錯(cuò)誤,不要感到奇怪。


垃圾回收器配置參數(shù).jpg

有這么多垃圾回收器和參數(shù),那我們到底用什么?在什么地方優(yōu)化呢?

目前,雖然 Java 的版本比較高,但是使用最多的還是 Java8。從 Java8 升級(jí)到高版本的 Java 體系,是有一定成本的,所以 CMS 垃圾回收器還會(huì)持續(xù)一段時(shí)間。

線上使用最多的垃圾回收器,就有 CMS 和 G1,以及 Java8 默認(rèn)的 Parallel Scavenge。

CMS 的設(shè)置參數(shù):-XX:+UseConcMarkSweepGC。

Java8 的默認(rèn)參數(shù):-XX:+UseParallelGC。

Java13 的默認(rèn)參數(shù):-XX:+UseG1GC。

STW(Stop the world)

你有沒有想過,如果在垃圾回收的時(shí)候(不管是標(biāo)記還是整理復(fù)制),又有新的對(duì)象進(jìn)入怎么辦?

為了保證程序不會(huì)亂套,最好的辦法就是暫停用戶的一切線程。也就是在這段時(shí)間,你是不能 new 對(duì)象的,只能等待。表現(xiàn)在 JVM 上就是短暫的卡頓,什么都干不了。這個(gè)頭疼的現(xiàn)象,就叫作 Stop the world。簡(jiǎn)稱 STW。
標(biāo)記階段,大多數(shù)是要 STW 的。如果不暫停用戶進(jìn)程,在標(biāo)記對(duì)象的時(shí)候,有可能有其他用戶線程會(huì)產(chǎn)生一些新的對(duì)象和引用,造成混亂。
現(xiàn)在的垃圾回收器,都會(huì)盡量去減少這個(gè)過程。但即使是最先進(jìn)的 ZGC,也會(huì)有短暫的 STW 過程。我們要做的就是在現(xiàn)有基礎(chǔ)設(shè)施上,盡量減少 GC 停頓。
你可能對(duì) STW 的影響沒有什么概念,我舉個(gè)例子來說明下。
某個(gè)高并發(fā)服務(wù)的峰值流量是 10 萬次/秒,后面有 10 臺(tái)負(fù)載均衡的機(jī)器,那么每臺(tái)機(jī)器平均下來需要 1w/s。假如某臺(tái)機(jī)器在這段時(shí)間內(nèi)發(fā)生了 STW,持續(xù)了 1 秒,那么本來需要 10ms 就可以返回的 1 萬個(gè)請(qǐng)求,需要至少等待 1 秒鐘。

stw.jpg

在用戶那里的表現(xiàn),就是系統(tǒng)發(fā)生了卡頓。如果我們的 GC 非常的頻繁,這種卡頓就會(huì)特別的明顯,嚴(yán)重影響用戶體驗(yàn)。
雖然說 Java 為我們提供了非常棒的自動(dòng)內(nèi)存管理機(jī)制,但也不能濫用,因?yàn)樗怯?STW 硬傷的。

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