深入理解Java虛擬機(jī)(三)--G1垃圾回收器

G1 GC,全稱Garbage-First Garbage Collector,從官網(wǎng)的描述中說(shuō)明G1是一種服務(wù)器端的垃圾收集器糙麦,應(yīng)用在多處理器和大容量?jī)?nèi)存環(huán)境中,在實(shí)現(xiàn)應(yīng)用高吞吐量的同時(shí)丛肮,盡可能的滿足垃圾收集暫停時(shí)間的要求(可預(yù)測(cè)停頓)喳资,停頓預(yù)測(cè)模型的意思是能夠支持指定在一個(gè)時(shí)間長(zhǎng)度為M毫秒的時(shí)間片段內(nèi)消耗在垃圾收集上的時(shí)間大概率不超過(guò)N毫秒這樣的目標(biāo)。并且部分回收過(guò)程是和應(yīng)用線程并發(fā)執(zhí)行腾供,采用復(fù)制和標(biāo)記整理算法來(lái)減少內(nèi)存碎片仆邓。

G1回收期與傳統(tǒng)的收集器不同的是,傳統(tǒng)的回收器要么是整個(gè)新生代(Minor GC)伴鳖,要么就是整個(gè)老年代(Major GC)要么是整個(gè)Java堆(Full GC)节值。而G1跳出這個(gè)邏輯,可以面向堆內(nèi)存中任何部分來(lái)組成回收集(Collection Set,CSet)進(jìn)行回收榜聂,衡量標(biāo)準(zhǔn)不再是它屬于哪一個(gè)分代搞疗,而是內(nèi)存哪塊區(qū)域存放的垃圾最多,回收收益最大须肆,這就是G1收集器的Mixed GC模式匿乃。雖然G1仍然保留新生代老年代的概念桩皿,但是新生代和老年代不再是固定不變的區(qū)域,它們都是一系列不用連續(xù)的動(dòng)態(tài)集合幢炸。

1.核心概念

(1)分區(qū)

傳統(tǒng)的GC收集器將連續(xù)的內(nèi)存空間劃分為新生代泄隔、老年代和永久代(JDK 8去除了永久代,引入了元空間Metaspace)宛徊,這種劃分的特點(diǎn)是各代的存儲(chǔ)地址是連續(xù)的佛嬉。

圖1

而G1將整個(gè)堆分為相同大小的分區(qū),每個(gè)分區(qū)都可能是Eden區(qū)闸天、Survivor區(qū)或老年代暖呕,同類型的Region可以不連續(xù)。

圖2

還有一些分區(qū)標(biāo)明了H苞氮,表示巨型對(duì)象(humongous object湾揽,H-obj),它們代表大小大于等于region一半的對(duì)象(后面會(huì)繼續(xù)說(shuō)明H-obj的特點(diǎn))笼吟。

(2)CardTable

Card Table是劃分堆的數(shù)據(jù)結(jié)構(gòu)钝腺,將整個(gè)堆內(nèi)存劃分成一個(gè)個(gè)大小為512字節(jié)的Card,并維護(hù)一個(gè)Card Table來(lái)存儲(chǔ)每張Card的標(biāo)識(shí)位赞厕。Card Table是單字節(jié)數(shù)組艳狐,每一個(gè)元素可以用來(lái)標(biāo)示所在老年代Card中對(duì)象是否持有新生代Card中對(duì)象的引用,有持有則稱該老年代Card是Dirty Card皿桑。Card Table不是G1獨(dú)有的結(jié)構(gòu)毫目。

圖3

Card Table目的是回收新生代時(shí)減少老年代的全堆空間掃描,從而高效GC诲侮。在回收新生代的過(guò)程中镀虐,老年代對(duì)象可能引用新生代的對(duì)象,所以在GC Roots Tracing過(guò)程中就需要全堆掃描存活對(duì)象沟绪,這就導(dǎo)致回收新生代代價(jià)太高刮便。Card Table的引入可以解決這個(gè)問(wèn)題,因?yàn)橹恍枰贑ard Table中尋找Dirty Card绽慈,并加入GC Roots中恨旱,Dirty Card掃描后便會(huì)將其標(biāo)識(shí)位清空(普通Card)。

每次引用發(fā)生變化時(shí)都需要更新Card Table坝疼,一般采用post-write barrier(引用發(fā)生變化后的處理邏輯)和處理線程來(lái)更新維護(hù)Card Table搜贤。

(3)RSet

RSet全稱Remembered Set,這是一種抽象概念钝凶,用來(lái)輔助GC過(guò)程仪芒,在分代式GC時(shí)用于記錄從非收集部分(Old Generation)指向收集部分(Young Generation、Old Generation)的指針集合的抽象數(shù)據(jù)結(jié)構(gòu)。RSet在Card Table的基礎(chǔ)上實(shí)現(xiàn)的掂名,每個(gè)Region都有一份据沈,這個(gè)RSet記錄的是xx Region的xx Card指向RSet所在Region,這是一種"points-into"(誰(shuí)引用了我的對(duì)象)的結(jié)構(gòu)饺蔑,而Card Table是"points-out"(我引用了誰(shuí)的對(duì)象)的結(jié)構(gòu)锌介。因此,RSet是一個(gè)Hash Table膀钠,Key是別的Region的起始地址掏湾,Value是一個(gè)集合裹虫,里面的元素是別的Region的Card Table Index肿嘲。

舉例來(lái)說(shuō),如果Region2的RSet里有一項(xiàng)的Key是Region1筑公,Value里有Index為1234的Card雳窟,意思是Region1的一個(gè)Card里有引用指向Region2。所以對(duì)Region2來(lái)說(shuō)匣屡,該RSet記錄的是“points-into”的關(guān)系封救,而Card Table仍然記錄了"points-out"的關(guān)系。

圖4

作用

在做YGC的時(shí)候捣作,除了Young Generation GC Root Tracing外誉结,只需要選定Young Generation Region的RSet作為根集,這些RSet記錄了Old->Young的跨代引用券躁,避免了掃描整個(gè)Old Generation Region惩坑。 而MixedGC的時(shí)候,Old Generation Region中記錄了Old->Old的RSet也拜,Young->Old的引用由掃描全部Young Generation Region得到以舒,這樣也不用掃描全部Old Generation Region。至于為什么RSet只記錄了Old->Old/Young引用是因?yàn)閅oung ?Generation的引用變化非常頻繁慢哈,GC時(shí)不如直接掃描Young ?Generation Region帶來(lái)的收益高(G1的YGC和MixedGC過(guò)程都會(huì)全盤(pán)掃描Young Generation Region)蔓钟。

每次引用發(fā)生變化都需要更新RSet,“引用發(fā)生變化”在程序中體現(xiàn)就是在給引用類型的字段賦值卵贱,如果每次給引用類型的字段賦值都要更新RSet滥沫,這帶來(lái)的額外開(kāi)銷實(shí)在太大,G1中采用post-write barrier和ConcurrentG1RefineThread實(shí)現(xiàn)了RSet的更新键俱。

(4)三色標(biāo)記法

基本算法

三色標(biāo)記法是一些垃圾收集器的標(biāo)記階段使用的算法佣谐,在GC Roots遍歷過(guò)程中,標(biāo)記出哪些對(duì)象是存活的方妖,哪些是垃圾(可回收)狭魂。

該算法將對(duì)象的狀態(tài)分成三種顏色:

白色:尚未訪問(wèn)過(guò)。

黑色:該對(duì)象已訪問(wèn)過(guò),而且該對(duì)象所引用的其他對(duì)象也全部訪問(wèn)過(guò)了雌澄。

灰色:該對(duì)象已訪問(wèn)過(guò)斋泄,但是該對(duì)象所引用的其他對(duì)象尚未全部訪問(wèn)完。

假設(shè)現(xiàn)在有白镐牺、灰炫掐、黑三個(gè)集合(表示當(dāng)前對(duì)象的顏色),其遍歷訪問(wèn)過(guò)程為:

初始時(shí)睬涧,所有對(duì)象都在【白色集合】中募胃。

將GC Roots直接引用到的對(duì)象放入【灰色集合】中。

從灰色集合中獲取對(duì)象:將本對(duì)象引用到的其他對(duì)象全部挪到【灰色集合】中畦浓;將本對(duì)象放入【黑色集合】里面痹束。

重復(fù)步驟3,直至【灰色集合】為空時(shí)結(jié)束讶请。

結(jié)束后祷嘶,仍在【白色集合】的對(duì)象即為GC Roots不可達(dá),可以進(jìn)行回收夺溢。

圖5

(5)Snapshot-At-The-Beginning(SATB)

在并發(fā)收集周期的第一個(gè)階段(初始標(biāo)記)是STW的论巍,會(huì)給所有的分區(qū)做個(gè)快照,后面的掃描都是按照這個(gè)快照進(jìn)行风响;在并發(fā)標(biāo)記周期的第二個(gè)階段嘉汰,并發(fā)標(biāo)記,這是收集線程和應(yīng)用線程同時(shí)進(jìn)行的状勤,這時(shí)候應(yīng)用線程就可能修改了某些引用的值鞋怀,導(dǎo)致上面那個(gè)快照不是完整的,因此G1就想了個(gè)辦法荧降,我把在這個(gè)期間對(duì)對(duì)象引用的修改都記錄動(dòng)作都記錄下來(lái)(有點(diǎn)像mysql的操作日志)接箫。

G1的SATB的設(shè)計(jì)使得重新標(biāo)記(remark)階段只需要掃描改動(dòng)的記錄,而CMS在remark階段需要重新掃描所有的線程棧和年輕代作為GC roots朵诫。

2.回收主要過(guò)程

G1主要包含的四種操作

(1)Young GC:Eden區(qū)域滿了后觸發(fā)辛友,并行收集整個(gè)新生代,且完全STW剪返。

(2)并發(fā)標(biāo)記周期:它的第一個(gè)階段初始化標(biāo)記和YGC一起發(fā)生废累,這個(gè)階段的目的就是找到回收價(jià)值最大的Region集合(垃圾很多,存活對(duì)象很少)脱盲,為接下來(lái)的Mixed GC服務(wù)邑滨。

(3)Mixed GC:回收所有年輕代的Region和部分老年代的Region,Mixed GC可能連續(xù)發(fā)生多次钱反。

(4)Full GC:非常慢掖看,會(huì)STW且回收所有類型的Region匣距。

2.1 Young GC

Young GC是STW的,在分配一般對(duì)象(非巨型對(duì)象)時(shí)哎壳,當(dāng)所有Eden Region使用達(dá)到最大閾值并且無(wú)法申請(qǐng)足夠內(nèi)存時(shí)毅待,會(huì)觸發(fā)一次Young GC。每次Young GC會(huì)回收所有Eden以及一個(gè)Survivor區(qū)归榕,并且將存活對(duì)象復(fù)制到Old區(qū)(涉及到晉升到老年代規(guī)則)以及另一個(gè)的Survivor區(qū)尸红。

Young GC一般過(guò)程:

掃描GC Roots根對(duì)象。

處理Dirty Card刹泄,更新RSet外里。ConcurrentG1RefineThread會(huì)對(duì)Dirty Card的分區(qū)進(jìn)行掃描更新日志緩沖區(qū)來(lái)更新RSet,但只會(huì)處理全局緩沖列表特石。作為補(bǔ)充盅蝗,所有被記錄但是還沒(méi)有被ConcurrentG1RefineThread處理的剩余緩沖區(qū),會(huì)在該階段處理县匠,變成已處理緩沖區(qū)(Processed Buffers)风科。

掃描RSet撒轮,掃描RSet中所有Old區(qū)對(duì)掃描到的Young區(qū)引用乞旦,避免了Old區(qū)全掃描。

拷貝掃描出的存活的對(duì)象到Survivor/Old區(qū)题山,將待回收的Region全部加入CSet進(jìn)行回收兰粉。

Global Concurrent Marking

Global Concurrent Marking是為Mixed GC提供標(biāo)記服務(wù)的(一般會(huì)將Global Concurrent Marking包含在Mixed GC過(guò)程中,這里單獨(dú)拆開(kāi)描述)顶瞳,用來(lái)統(tǒng)計(jì)出收集收益最高的若干老年代Region玖姑。主要使用Region的bitmap結(jié)構(gòu)和TAMS指針來(lái)支持整個(gè)標(biāo)記過(guò)程,下節(jié)開(kāi)始會(huì)按照Global Concurrent Marking的四個(gè)步驟對(duì)兩個(gè)bitmap和兩個(gè)TAMS指針進(jìn)行詳細(xì)說(shuō)明慨菱。

四個(gè)步驟:

初始標(biāo)記(Initial Marking焰络,STW)

并發(fā)標(biāo)記(Concurrent Marking)

最終標(biāo)記(Remark,STW)

清理階段(Cleanup)

bitmap結(jié)構(gòu):prevBitmap和nextBitmap符喝。preBitmap記錄是上一輪Concurrent Marking后的老年代Region對(duì)象的標(biāo)記狀態(tài)闪彼,因?yàn)樯弦惠喴呀?jīng)完成,所以可以直接使用prevBitmap协饲;nextBitmap是當(dāng)前這一輪Concurrent Marking的結(jié)果畏腕,尚未完成,不能使用茉稠。當(dāng)前這一輪Concurrent Marking結(jié)束后描馅,prevBitmap和nextBitmap互換。

TAMS指針:prevTAMS和nextTAMS而线。prevTAMS記錄的上一輪Concurrent Marking后的老年代Region對(duì)象的top指針(指向已使用部分最大地址)位置铭污;nextTAMS記錄的是當(dāng)前這一輪Concurrent Marking的top指針恋日。當(dāng)前這一輪Concurrent Marking結(jié)束后,prevTAMS和nextTAMS互換嘹狞。

初始標(biāo)記(Initial Marking, STW)

這階段僅僅只是標(biāo)記GC Roots能直接關(guān)聯(lián)到的對(duì)象谚鄙,直接關(guān)聯(lián)到的對(duì)象就是下圖Region內(nèi)存已使用的部分中(botoom和top指針之間);并修改nextTAMS的指針指向?yàn)閠op和創(chuàng)建nextBitmap刁绒,就緒后讓下一階段應(yīng)用線程并發(fā)運(yùn)行時(shí)闷营,能在可用的Region中創(chuàng)建新對(duì)象。這是STW的過(guò)程知市,共用了Young GC的暫停傻盟,這是因?yàn)樗麄兛梢詮?fù)用GC Roots Scan操作,所以可以說(shuō)Global Concurrent Marking是伴隨Young GC而發(fā)生的嫂丙。

圖6

并發(fā)標(biāo)記(Concurrent Marking)

在并發(fā)標(biāo)記階段娘赴,下圖GC線程工作在prevTAMS和nextTAMS 之間,對(duì)堆里的對(duì)象進(jìn)行GC Roots可達(dá)性分析跟啤,邊標(biāo)記邊更新nextBitmap诽表,黑色對(duì)應(yīng)的是存活對(duì)象,白色對(duì)應(yīng)的垃圾對(duì)象隅肥。應(yīng)用線程是工作在bottom和top之間的竿奏,和GC線程工作區(qū)間不重疊的地方是新對(duì)象的創(chuàng)建(創(chuàng)建新對(duì)象分配在nextTAMS和top指針之間),而重疊的地方可能會(huì)造成對(duì)象誤標(biāo)腥放,所以還要同時(shí)處理pre-write barrier記錄的在并發(fā)時(shí)有引用變動(dòng)的對(duì)象泛啸,以上來(lái)保證SATB。

圖7

最終標(biāo)記(Remark, STW)

這一階段對(duì)應(yīng)用線程做另一個(gè)短暫的暫停秃症,用于處理并發(fā)標(biāo)記階段結(jié)束后仍遺留下來(lái)的最后那少量的pre-write barrier引用變動(dòng)記錄候址,這是因?yàn)椴l(fā)標(biāo)記階段處理pre-write barrier引用變動(dòng)的時(shí)候應(yīng)用線程還可能持續(xù)在產(chǎn)生新的引用變動(dòng),所以必須STW一會(huì)來(lái)處理剩余的最后一小部分記錄种柑。

圖8

清理階段(Cleanup)

這階段包括了清理和回收岗仑。這階段的清理是將nextBitmap和prevBitmap合并,并統(tǒng)計(jì)該Region有多少對(duì)象存活聚请,如果該Region沒(méi)有對(duì)象存活荠雕,那么就會(huì)在該階段的回收部分進(jìn)行整體回收,并加入到可分配Region列表中良漱。最后將prevBitmap和nextBitmap互換舞虱,prevTAMS和nextTAMS互換。

圖9

以上便是G1收集器進(jìn)行YGC的過(guò)程母市。

2.2 Mixed GC特點(diǎn):

(1)Mixed GC是完全STW的矾兜,它根據(jù)用戶設(shè)置的停頓時(shí)間目標(biāo),可以選擇回收所有年輕代患久,以及部分老年代Region集合(CSet)

(2)在一次完整的全局并發(fā)標(biāo)記周期后椅寺,如果滿足觸發(fā)Mixed GC的條件浑槽,那么就會(huì)執(zhí)行Mixed GC,并且Mixed GC可能會(huì)執(zhí)行多次返帕。當(dāng)整個(gè)堆的使用率超過(guò)指定百分比時(shí)桐玻,GC會(huì)啟動(dòng)新一輪的并發(fā)標(biāo)記周期。

何時(shí)發(fā)生Mixed GC?被幾個(gè)參數(shù)控制:

(1)G1HeapWastePercent:在global concurrent marking結(jié)束之后荆萤,我們可以知道老年代中有多少空間要被回收彩扔,如果老年代中可回收的內(nèi)存/老年代總內(nèi)存到此參數(shù)(默認(rèn)5%)剿涮,才會(huì)發(fā)生Mixed GC铺厨;否則發(fā)生YGC窍株。

(2)有多少分區(qū)會(huì)被認(rèn)定為垃圾分區(qū)。-XX:G1MixedGCLiveThresholdPercent=n表示如果一個(gè)分區(qū)存活對(duì)象比例超過(guò)n敞峭,就不會(huì)被選為垃圾分區(qū)踊谋。值越大越容易被當(dāng)做垃圾分區(qū)。通過(guò)它可以控制每次混合收集的分區(qū)個(gè)數(shù)

(3)G1在一個(gè)并發(fā)周期中旋讹,最多經(jīng)歷幾次Mixed GC殖蚕,這個(gè)可以通過(guò)-XX:G1MixedGCCountTarget=n設(shè)置,默認(rèn)是8沉迹,如果減小這個(gè)值睦疫,可以增加每次混合收集收集的分區(qū)數(shù),但是可能會(huì)導(dǎo)致停頓時(shí)間過(guò)長(zhǎng)胚股;

(4)最大停頓時(shí)間MaxGCPauseMillis笼痛,默認(rèn)是200ms裙秋,如果實(shí)際運(yùn)行時(shí)間比這個(gè)值小琅拌,G1就能收集更多的分區(qū)

在選定CSet后,G1在執(zhí)行Evacuation階段時(shí)摘刑,其實(shí)就跟YGC的算法類似进宝,采用并行復(fù)制算法把CSet里每個(gè)Region中的存活對(duì)象拷貝到新的Region里,然后回收掉這些Region枷恕,整個(gè)過(guò)程完全STW党晋。這個(gè)過(guò)程相當(dāng)于進(jìn)行壓縮,因此G1出現(xiàn)碎片化的頻率比CMS小得多

Evacuation拷貝&清理

Evacuation徐块,就是將存活對(duì)象拷貝到1個(gè)或多個(gè)Region中未玻,并清理RSet中的Region;YGC和Mixed GC中都包含這個(gè)過(guò)程胡控。

STW階段扳剿,Evacuation過(guò)程有兩種模式:既可能由YGC來(lái)完成,只回收所有的年輕代(GC日志樣例:[GC pause (young)])昼激。也可能是Mixed GC來(lái)完成的庇绽,即回收所有的年輕代外加根據(jù)global concurrent marking統(tǒng)計(jì)得出收集收益高的部分老年代Region(GC日志樣例:[GC Pause (mixed)]):

也就是說(shuō)锡搜,并發(fā)標(biāo)記周期之后不一定發(fā)生Mixed GC,需要看是否滿足Mixed GC的條件瞧掺。

2.3 Full GC

在G1的正常工作流程中沒(méi)有Full GC的概念耕餐,老年代的收集全靠Mixed GC來(lái)完成。

(1)但是辟狈,畢竟Mixed GC有搞不定的時(shí)候肠缔,如果Mixed GC實(shí)在無(wú)法跟上程序分配內(nèi)存的速度,導(dǎo)致老年代填滿無(wú)法繼續(xù)進(jìn)行Mixed GC哼转,就會(huì)切換到G1之外的Serial Old GC來(lái)收集整個(gè)堆桩砰,進(jìn)入這種狀態(tài)的G1就跟-XX:+UseSerialGC的Full GC一樣(背后的核心代碼是兩者共用的),在發(fā)生Full GC時(shí)是單線程運(yùn)行的释簿,因此應(yīng)該盡量避免發(fā)生Full GC亚隅。

(2)這就是為什么不建議G1模式下參數(shù)-XX:MaxGCPauseMillis=200 的值設(shè)置太小,如果設(shè)置太小庶溶,可能導(dǎo)致每次Mixed GC只能回收很小一部分Region煮纵,最終可能無(wú)法跟上程序分配內(nèi)存的速度,從而觸發(fā)Full GC偏螺。

(3)順帶一提行疏,G1模式下的System.gc()默認(rèn)也是Full GC,套像。只有加上參數(shù) -XX:+ExplicitGCInvokesConcurrent 時(shí)G1才會(huì)用自身的并發(fā)GC來(lái)執(zhí)行System.gc()酿联;

幾種導(dǎo)致Full GC的情況:

(1)concurrent mode failure

說(shuō)明:G1并發(fā)標(biāo)記期間,如果在標(biāo)記結(jié)束前夺巩,老年代被填滿贞让,G1 會(huì)放棄標(biāo)記。

解決:增加堆大辛喳张;調(diào)整并發(fā)周期,例如增加并發(fā)標(biāo)記的線程數(shù)美澳,讓并發(fā)標(biāo)記盡快結(jié)束销部;更早進(jìn)行并發(fā)周期,默認(rèn)是整堆45%占用就開(kāi)始

(2)晉升失敗

說(shuō)明:混合收集過(guò)程中制跟,伴隨著年輕代垃圾收集舅桩,進(jìn)行清理老年代空間,如果這個(gè)時(shí)候清理的速度小于消耗的速度雨膨,導(dǎo)致老年代不夠用擂涛,那么會(huì)發(fā)生晉升失敗。

(3)疏散失敗--to space overflow

說(shuō)明:YGC的時(shí)候哥放,如果 Survivor 和 Old 區(qū)沒(méi)有足夠的空間容納所有的存活對(duì)象

(4)大對(duì)象分配失敗歼指,沒(méi)有連續(xù)可用的region存放巨型對(duì)象(G1會(huì)挑選出1組物理連續(xù)的可用 Region, 相加后只要能夠確钡粒總大小可以存放這個(gè)巨型對(duì)象,就會(huì)分配給這個(gè)巨型對(duì)象踩身。反之如果沒(méi)有能夠找到符合條件的連續(xù)可用Region 胀茵,會(huì)執(zhí)行1次Full GC壓縮堆)

(5)元空間 = MaxMetaspaceSize

3.巨型對(duì)象的管理

巨型對(duì)象:一個(gè)對(duì)象的大小超過(guò)region大小的一半。

特點(diǎn):

巨型對(duì)象直接分配到了老年代挟阻,防止了反復(fù)拷貝移動(dòng)琼娘。

在JDK8u40之前,巨型對(duì)象的回收只能在并發(fā)收集周期的clean up或FULL GC過(guò)程中被回收附鸽,在JDK8u40(包括這個(gè)版本)之后脱拼,一旦沒(méi)有任何其他對(duì)象引用巨型對(duì)象,那么巨型對(duì)象也可以在YGC中被回收

巨型對(duì)象存放在連續(xù)可用的region坷备,并且獨(dú)占region

如果發(fā)現(xiàn)有很多由于巨型對(duì)象分配引起的連續(xù)并發(fā)周期熄浓,并且堆已經(jīng)碎片化了(明明空間夠,卻觸發(fā)full GC)省撑,可以考慮使用-XX:G1HeapRegionSize命令增大分區(qū)大小赌蔑,將巨型對(duì)象變?yōu)槠胀▽?duì)象,從而減少巨型對(duì)象分配對(duì)GC的影響竟秫。

最后:打一個(gè)小廣告娃惯,后續(xù)的文章會(huì)在微信公眾號(hào)“程序員之家QAQ”推送,歡迎大家搜索關(guān)注~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末肥败,一起剝皮案震驚了整個(gè)濱河市趾浅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌馒稍,老刑警劉巖皿哨,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異筷黔,居然都是意外死亡往史,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門佛舱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人挨决,你說(shuō)我怎么就攤上這事请祖。” “怎么了脖祈?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵肆捕,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我盖高,道長(zhǎng)慎陵,這世上最難降的妖魔是什么眼虱? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮席纽,結(jié)果婚禮上捏悬,老公的妹妹穿的比我還像新娘。我一直安慰自己润梯,他們只是感情好过牙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著纺铭,像睡著了一般寇钉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上舶赔,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天扫倡,我揣著相機(jī)與錄音,去河邊找鬼竟纳。 笑死镊辕,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蚁袭。 我是一名探鬼主播征懈,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼揩悄!你這毒婦竟也來(lái)了卖哎?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤删性,失蹤者是張志新(化名)和其女友劉穎亏娜,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蹬挺,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡维贺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了巴帮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片溯泣。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖榕茧,靈堂內(nèi)的尸體忽然破棺而出垃沦,到底是詐尸還是另有隱情,我是刑警寧澤用押,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布肢簿,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏池充。R本人自食惡果不足惜桩引,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望收夸。 院中可真熱鬧坑匠,春花似錦、人聲如沸咱圆。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)序苏。三九已至手幢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間忱详,已是汗流浹背围来。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留匈睁,地道東北人监透。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像航唆,于是被迫代替她去往敵國(guó)和親胀蛮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

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