垃圾回收器和內(nèi)存分配策略

本文作者:李敏,叩丁狼高級(jí)講師。原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明出處。

4. 垃圾回收器和內(nèi)存分配策略

GC(Garbage Collection)的歷史比java久遠(yuǎn).1960年誕生于MIT的Lisp是第一門真正使用內(nèi)存動(dòng)態(tài)分配和垃圾收集技術(shù)的語(yǔ)言.GC一直致力于解決的問(wèn)題:

哪些內(nèi)存需要回收(what)?
什么時(shí)候回收(when)?
如何回收(how)?

在虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)中,最頻繁使用的是堆,管理的內(nèi)存最大的一塊,還是堆.JVM堆是垃圾收集器管理的主要區(qū)域.因此很多時(shí)候也被稱作"GC堆".

4.1 為什么要了解垃圾回收

經(jīng)過(guò)半個(gè)多世紀(jì)的發(fā)展,目前內(nèi)存的動(dòng)態(tài)分配與內(nèi)存回收技術(shù)已經(jīng)相當(dāng)成熟,一切看起來(lái)都進(jìn)入了"自動(dòng)化"時(shí)代,那為什么我們還要去了解GC和內(nèi)存分配呢?

當(dāng)需要排查各種內(nèi)存溢出,內(nèi)存泄漏問(wèn)題時(shí),當(dāng)垃圾收集成為系統(tǒng)達(dá)到更高并發(fā)量的瓶頸時(shí),我們就需要對(duì)這些"自動(dòng)化"的技術(shù)實(shí)施必要的監(jiān)控和調(diào)節(jié).

程序計(jì)數(shù)器、虛擬機(jī)棧、本地方法棧 3 個(gè)區(qū)域隨線程生滅(因?yàn)槭蔷€程私有),棧中的棧幀隨著方法的進(jìn)入和退出而有條不紊地執(zhí)行著出棧和入棧操作。所以這三塊區(qū)域的內(nèi)存,我們不需要過(guò)多考慮回收問(wèn)題.

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

4.2 對(duì)象存活判斷

垃圾回收器,會(huì)針對(duì)沒(méi)有任何引用的”死去”的對(duì)象進(jìn)行回收.垃圾收集器在對(duì)堆進(jìn)行回收前,第一件事情就是要確定這些對(duì)象之后哪些還"存活",哪些已經(jīng)變成垃圾"死去".

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

給對(duì)象添加一個(gè)引用計(jì)數(shù)器.有一個(gè)地方引用時(shí),計(jì)數(shù)器+1,失去引用,計(jì)數(shù)器-1.為0表示就是垃圾.但是難以解決循環(huán)引用問(wèn)題.比如A只有B,B只有A.主流的java虛擬機(jī)沒(méi)有選用引用計(jì)數(shù)算法來(lái)判定對(duì)象的生死.

測(cè)試代碼:

public class ReferenceCountingGC {
      public Object instance = null;
      private static final int _1MB = 1024 * 1024;
      // 使用該變量表示在內(nèi)存中占用空間,以便可以方便觀察日志.
      private byte[] bigSize = new byte[2 * _1MB];
      public static void main(String[] args) {
          ReferenceCountingGC objA = new ReferenceCountingGC();
          ReferenceCountingGC objB = new ReferenceCountingGC();
          objA.instance = objB;
          objB.instance = objA;
          // 設(shè)置兩個(gè)變量為null,那么對(duì)象理論上來(lái)說(shuō)就是垃圾.
          objA = null;
          objB = null;
          // 假設(shè)這里發(fā)生GC,查看日志,觀察兩個(gè)對(duì)象是否被回收.
          System.gc();
  }
}

運(yùn)行參數(shù):-XX:+PrintGCDetails

測(cè)試結(jié)果:

[GC [PSYoungGen: 5427K->584K(38400K)] 5427K->584K(124416K), 0.0010157     secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC [PSYoungGen: **584K->0K**(38400K)] [ParOldGen: 0K->465K(86016K)] 584K->465K(124416K) [PSPermGen: 2557K->2556K(21504K)], 0.0076511 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
Heap
 PSYoungGen      total 38400K, used 998K [0x00000007d5e80000,     0x00000007d8900000, 0x0000000800000000)
  eden space 33280K, 3% used [0x00000007d5e80000,0x00000007d5f79a60,0x00000007d7f00000)
  from space 5120K, 0% used [0x00000007d7f00000,0x00000007d7f00000,0x00000007d8400000)
  to   space 5120K, 0% used [0x00000007d8400000,0x00000007d8400000,0x00000007d8900000)
 ParOldGen       total 86016K, used 465K [0x0000000781c00000, 0x0000000787000000, 0x00000007d5e80000)
  object space 86016K, 0% used [0x0000000781c00000,0x0000000781c74508,0x0000000787000000)
 PSPermGen       total 21504K, used 2563K [0x000000077ca00000, 0x000000077df00000, 0x0000000781c00000)
  object space 21504K, 11% used [0x000000077ca00000,0x000000077cc80f38,0x000000077df00000)

可以先嘗試性的看一下GC的日志,可以發(fā)現(xiàn),在年輕代,發(fā)生了GC之后,年輕代的空間使用為0,的的確確表示對(duì)象被GC了.所以從這個(gè)案例我們可以證明,JVM虛擬機(jī)沒(méi)有使用引用計(jì)數(shù)算法.

4.2.2 可達(dá)性算法

通過(guò)一系列的 ‘GC Roots’ 的對(duì)象作為起始點(diǎn),從這些節(jié)點(diǎn)出發(fā)所走過(guò)的路徑稱為引用鏈。當(dāng)一個(gè)對(duì)象到 GC Roots 沒(méi)有任何引用鏈相連的時(shí)候說(shuō)明對(duì)象不可用,如下圖,GC Roots對(duì)象不可達(dá)的對(duì)象,就是可以回收的垃圾對(duì)象.


20.png

可作為 GC Roots 的對(duì)象:
1.虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象
2.方法區(qū)中類靜態(tài)屬性引用的對(duì)象
3.方法區(qū)中常量引用的對(duì)象
4.本地方法棧中JNI(即一般說(shuō)的 Native 方法) 引用的對(duì)象

4.2.2.1 HotSpot的可達(dá)性分析

HotSpot虛擬機(jī)在實(shí)現(xiàn)上面的算法的時(shí)候,必須要經(jīng)過(guò)更嚴(yán)格的考量,才能保證虛擬機(jī)高效運(yùn)行.比如,在上面的可達(dá)性分析中,就存在執(zhí)行效率的問(wèn)題:

1.從GC Roots節(jié)點(diǎn)找引用鏈,可是現(xiàn)在很多應(yīng)用的引用比較復(fù)雜,比如方法區(qū)就有數(shù)百兆,如果要逐個(gè)檢查這里面的引用,必然消耗很多的時(shí)間.
2.為了保證整個(gè)分析期間整個(gè)執(zhí)行系統(tǒng)被凍結(jié),而不產(chǎn)生新的引用,會(huì)導(dǎo)致java執(zhí)行線程停頓(stop the world).

為了解決上面的兩個(gè)問(wèn)題:

1.枚舉根節(jié)點(diǎn),使用一組OopMap的數(shù)據(jù)結(jié)構(gòu)來(lái)存放對(duì)象引用,這個(gè)數(shù)據(jù)結(jié)構(gòu)在類加載完成的時(shí)候,就已經(jīng)計(jì)算出來(lái)了,GC在掃描的時(shí)候就可以得知這些信息,從而降低GC Roots時(shí)間以及減少停頓時(shí)間.

2.OopMap中的引用關(guān)系可能會(huì)變化.或者OopMap的指令太多,反而需要更多的空間.此時(shí)解決方案是,OopMap會(huì)根據(jù)虛擬機(jī)選定的安全點(diǎn)(safepoint,可以簡(jiǎn)單理解為執(zhí)行到哪一行),在這個(gè)安全點(diǎn)內(nèi)去生成指令的OopMap.在GC 的時(shí)候,驅(qū)使所有的線程都"跑"到最近的安全點(diǎn),STW才發(fā)生,應(yīng)用才停頓.

3.對(duì)于掛起的線程來(lái)說(shuō),比如處于sleep或者blocked狀態(tài)的,是不能"跑"到安全點(diǎn)的,那么此時(shí)解決方案就是,增大安全域(Safe Region).如果線程已經(jīng)達(dá)到安全域,做一個(gè)標(biāo)記,GC就不需要管這些線程.

4.2.3 對(duì)象自我拯救

即使在可達(dá)性分析算法中不可達(dá)的對(duì)象,也并非是“非死不可”的,這時(shí)候它們暫時(shí)出于“緩刑”階段,一個(gè)對(duì)象的真正死亡至少要經(jīng)歷兩次標(biāo)記過(guò)程:

1.如果對(duì)象在進(jìn)行中可達(dá)性分析后發(fā)現(xiàn)沒(méi)有與 GC Roots 相連接的引用鏈,那他將會(huì)被第一次標(biāo)記并且進(jìn)行一次篩選,篩選條件是此對(duì)象是否有必要執(zhí)行 finalize() 方法。當(dāng)對(duì)象沒(méi)有覆蓋 finalize() 方法,或者 finalize() 方法已經(jīng)被虛擬機(jī)調(diào)用過(guò),虛擬機(jī)將這兩種情況都視為“沒(méi)有必要執(zhí)行”,這種情況就活不了。

2.如果這個(gè)對(duì)象被判定為有必要執(zhí)行 finalize() 方法,那么這個(gè)對(duì)象竟會(huì)放置在一個(gè)叫做 F-Queue 的隊(duì)列中,并在稍后由一個(gè)由虛擬機(jī)自動(dòng)建立的、低優(yōu)先級(jí)的 Finalizer 線程去執(zhí)行它。這里所謂的“執(zhí)行”是指虛擬機(jī)會(huì)出發(fā)這個(gè)方法,并不承諾或等待他運(yùn)行結(jié)束。finalize() 方法是對(duì)象逃脫死亡命運(yùn)的最后一次機(jī)會(huì),稍后 GC 將對(duì) F-Queue 中的對(duì)象進(jìn)行第二次小規(guī)模的標(biāo)記,如果對(duì)象要在 finalize() 中成功拯救自己 —— 只要重新與引用鏈上的任何一個(gè)對(duì)象簡(jiǎn)歷關(guān)聯(lián)即可。
finalize() 方法只會(huì)被系統(tǒng)自動(dòng)調(diào)用一次。

建議大家盡量避免使用它,因?yàn)樗皇莄/c++的析構(gòu)函數(shù),而是java剛誕生的時(shí)候?yàn)榱耸筩/c++程序員更容易接受它所作出的一個(gè)妥協(xié).代價(jià)高昂,不確定性大,無(wú)法保證各個(gè)對(duì)象的調(diào)用順序.大家完全可以忘掉java語(yǔ)言中有這個(gè)方法的存在.

4.2.4 再談引用

無(wú)論引用計(jì)數(shù)算法或者是可達(dá)性分析算法,都是用的是引用.在JDK1.2之前,java中的引用定義為,如果reference類型的數(shù)據(jù)中存儲(chǔ)的數(shù)值代表的是另外一塊內(nèi)存的起始地址,就稱這塊內(nèi)存代表著一個(gè)引用.在JDK1.2之后,java對(duì)引用的概念進(jìn)行了擴(kuò)充,

1.強(qiáng)引用:類似于 Object obj = new Object(); 創(chuàng)建的,只要強(qiáng)引用在就不回收。

2.軟引用:SoftReference 類實(shí)現(xiàn)軟引用。在系統(tǒng)要發(fā)生內(nèi)存溢出異常之前,將會(huì)把這些對(duì)象列進(jìn)回收范圍之中進(jìn)行二次回收。

3.弱引用:WeakReference 類實(shí)現(xiàn)弱引用。對(duì)象只能生存到下一次垃圾收集之前。在垃圾收集器工作時(shí),無(wú)論內(nèi)存是否足夠都會(huì)回收掉只被弱引用關(guān)聯(lián)的對(duì)象。

4.虛引用:PhantomReference 類實(shí)現(xiàn)虛引用。無(wú)法通過(guò)虛引用獲取一個(gè)對(duì)象的實(shí)例,為一個(gè)對(duì)象設(shè)置虛引用關(guān)聯(lián)的唯一目的就是能在這個(gè)對(duì)象被收集器回收時(shí)收到一個(gè)系統(tǒng)通知。

我們可以使用上面四種引用,根據(jù)內(nèi)存是足夠或者緊張的應(yīng)用場(chǎng)景,具體的來(lái)選擇使用何種引用方式.

4.3 方法區(qū)回收

方法區(qū)(Hotspot中的永久代),也是可以存在垃圾回收的,盡管在java虛擬機(jī)規(guī)范中確實(shí)表示可以不要求虛擬機(jī)在方法區(qū)實(shí)現(xiàn)垃圾回收.且性價(jià)比比較低.我們可以通過(guò)配置相關(guān)參數(shù),在何時(shí)的時(shí)候來(lái)回收永久代,以保證不會(huì)溢出.

1.永久代回收信息:廢棄的常量和無(wú)用的類.
廢棄的常量:沒(méi)有任何地方使用常量引用,也沒(méi)有任何地方使用常量對(duì)應(yīng)的字面量.
無(wú)用的類:該類所有的實(shí)例被回收,加載該類的類加載器被回收.該類的字節(jié)碼對(duì)象沒(méi)有被任何地方引用.

2.在大量使用反射,動(dòng)態(tài)代理,CGLib等ByteCode框架,動(dòng)態(tài)生成JSP以及OSGi這類頻繁自定義ClassLoader的場(chǎng)景都需要虛擬機(jī)具備類卸載的功能.

4.4 垃圾收集算法

知道了要回收哪些對(duì)象之后,就是具體的垃圾回收了.虛擬機(jī)規(guī)范中沒(méi)有限定只能使用何種方式去清理垃圾,所以,不同的虛擬機(jī)可以使用不同的回收算法或者組合使用.

4.4.1 標(biāo)記-清除

標(biāo)記-清除”(Mark-Sweep)算法,如它的名字一樣,算法分為“標(biāo)記”和“清除”兩個(gè)階段:首先標(biāo)記出所有需要回收的對(duì)象,在標(biāo)記完成后統(tǒng)一回收掉所有被標(biāo)記的對(duì)象。之所以說(shuō)它是最基礎(chǔ)的收集算法,是因?yàn)楹罄m(xù)的收集算法都是基于這種思路并對(duì)其缺點(diǎn)進(jìn)行改進(jìn)而得到的。

21.png

它的主要缺點(diǎn)有兩個(gè):一個(gè)是效率問(wèn)題,標(biāo)記和清除過(guò)程的效率都不高;另外一個(gè)是空間問(wèn)題,標(biāo)記清除之后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會(huì)導(dǎo)致,當(dāng)程序在以后的運(yùn)行過(guò)程中需要分配較大對(duì)象時(shí)無(wú)法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動(dòng)作。

此算法需要暫停整個(gè)應(yīng)用(Stop The World).

4.4.2 復(fù)制

“復(fù)制”(Copying)的收集算法,它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當(dāng)這一塊的內(nèi)存用完了,就將還存活著的對(duì)象復(fù)制到另外一塊上面,然后再把已使用過(guò)的內(nèi)存空間一次清理掉。

22.png

這樣使得每次都是對(duì)其中的一塊進(jìn)行內(nèi)存回收,內(nèi)存分配時(shí)也就不用考慮內(nèi)存碎片等復(fù)雜情況,只要移動(dòng)堆頂指針,按順序分配內(nèi)存即可,實(shí)現(xiàn)簡(jiǎn)單,運(yùn)行高效。只是這種算法的代價(jià)是將內(nèi)存縮小為原來(lái)的一半,持續(xù)復(fù)制長(zhǎng)生存期的對(duì)象則導(dǎo)致效率降低。

4.4.3 標(biāo)記-整理

“標(biāo)記-整理”(Mark-Compact)算法,標(biāo)記過(guò)程仍然與“標(biāo)記-清除”算法一樣,但后續(xù)步驟不是直接對(duì)可回收對(duì)象進(jìn)行清理,而是讓所有存活的對(duì)象都向一端移動(dòng),然后直接清理掉端邊界以外的內(nèi)存.

23.png

4.4.4 分代收集

GC分代的基本假設(shè):絕大部分對(duì)象的生命周期都非常短暫,存活時(shí)間短, 并且不同的對(duì)象的生命周期是不一樣的。

“分代收集”(Generational Collection)算法,把Java堆分為新生代和老年代,這樣就可以根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴?。在新生代中,每次垃圾收集時(shí)都發(fā)現(xiàn)有大批對(duì)象死去,只有少量存活,那就選用復(fù)制算法,只需要付出少量存活對(duì)象的復(fù)制成本就可以完成收集。而老年代中因?yàn)閷?duì)象存活率高、沒(méi)有額外空間對(duì)它進(jìn)行分配擔(dān)保,就必須使用“標(biāo)記-清理”或“標(biāo)記-整理”算法來(lái)進(jìn)行回收。

4.4.4.1 為什么要分代

為什么需要把堆分代?不分代不能完成他所做的事情么?其實(shí)不分代完全可以,分代的唯一理由就是優(yōu)化GC性能。

JVM在程序運(yùn)行過(guò)程當(dāng)中,會(huì)創(chuàng)建大量的對(duì)象,這些對(duì)象,大部分是短周期的對(duì)象,小部分是長(zhǎng)周期的對(duì)象,對(duì)于短周期的對(duì)象,需要頻繁地進(jìn)行垃圾回收 以保證無(wú)用對(duì)象盡早被釋放掉,對(duì)于長(zhǎng)周期對(duì)象,則不需要頻率垃圾回收以確保無(wú)謂地垃圾掃描檢測(cè)。如果沒(méi)有分代,那我們所有的對(duì)象都在一塊,GC的時(shí)候我們要找到哪些對(duì)象沒(méi)用,這樣就會(huì)對(duì)堆的所有區(qū)域進(jìn)行掃描.

為解決這種矛盾,Sun JVM的內(nèi)存管理采用分代的策略。如果分代的話,我們把新創(chuàng)建的對(duì)象放到某一地方,當(dāng)GC的時(shí)候先把這塊存“朝生夕死”對(duì)象的區(qū)域進(jìn)行回收,這樣就會(huì)騰出很大的空間出來(lái)。

24.png

新生代和老年代存在于堆中,而永久代是方法區(qū)的實(shí)現(xiàn)方式(java7逐步取代永久代,在java8之后,完全去掉永久代,使用Meta space元數(shù)據(jù)區(qū)).

4.4.4.2 新生代

新生代/年輕代(Young Gen):年輕代主要存放新創(chuàng)建的對(duì)象,內(nèi)存大小相對(duì)會(huì)比較小,垃圾回收會(huì)比較頻繁。年輕代分成1個(gè)Eden Space和2個(gè)Survivor Space。

當(dāng)對(duì)象在堆創(chuàng)建時(shí),將進(jìn)入年輕代的Eden(伊甸園) Space。垃圾回收器進(jìn)行垃圾回收時(shí),掃描Eden Space和A Survivor(幸存區(qū)) Space,如果對(duì)象仍然存活,則復(fù)制到B Survivor Space,如果B Survivor Space已經(jīng)滿,則復(fù)制到Old Gen。同時(shí),在掃描A Survivor Space時(shí),如果對(duì)象已經(jīng)經(jīng)過(guò)了幾次的掃描仍然存活,JVM認(rèn)為其為一個(gè)持久化對(duì)象,則將其移到Old Gen。掃描完畢后,JVM將Eden Space和A Survivor Space清空,然后交換A和B的角色(即下次垃圾回收時(shí)會(huì)掃描Eden Space和B Survivor Space。這么做主要是為了減少內(nèi)存碎片的產(chǎn)生。

Young Gen垃圾回收時(shí),采用將存活對(duì)象復(fù)制到到空的Survivor Space的方式來(lái)確保盡量不存在內(nèi)存碎片,采用空間換時(shí)間的方式來(lái)加速內(nèi)存中不再被持有的對(duì)象盡快能夠得到回收。

4.4.4.3 老年代

老年代/年老代(Tenured Gen):年老代主要存放JVM認(rèn)為生命周期比較長(zhǎng)的對(duì)象(經(jīng)過(guò)幾次的Young Gen的垃圾回收后仍然存在),內(nèi)存大小相對(duì)會(huì)比較大,垃圾回收也相對(duì)沒(méi)有那么頻繁(譬如可能幾個(gè)小時(shí)一次)。年老代主要采用壓縮的方式來(lái)避免內(nèi)存碎片 (將存活對(duì)象移動(dòng)到內(nèi)存片的一邊,也就是內(nèi)存整理)。當(dāng)然,有些垃圾回收器(譬如CMS垃圾回收器)出于效率的原因,可能會(huì)不進(jìn)行壓縮。

4.4.4.4 虛擬機(jī)如何管理新生代和老年代

虛擬機(jī)一般是這樣管理新生代和老年代的:

1.當(dāng)一個(gè)對(duì)象被創(chuàng)建的時(shí)候(new)首先會(huì)在年輕代的Eden區(qū)被創(chuàng)建,直到當(dāng)GC的時(shí)候,根據(jù)可達(dá)性算法,看一個(gè)對(duì)象是否消亡,沒(méi)有消亡的對(duì)象會(huì)被放入新生代的Survivor區(qū),消亡的直接被Minor GC(次要的,普通的GC) Kill掉.

2.進(jìn)入到Survivor區(qū)的對(duì)象也不是安全的,當(dāng)下一次Minor GC來(lái)的時(shí)候還是會(huì)檢查Enden和Survivor存放對(duì)象區(qū)域中對(duì)象是否存活,存活放入另外一塊Survivor區(qū)域.

3.當(dāng)2個(gè)Survivor區(qū)切換幾次以后,會(huì)直接進(jìn)入老年代,當(dāng)然進(jìn)入到老年代也不是安全的,當(dāng)老年代內(nèi)存空間不足的時(shí)候,會(huì)觸發(fā)Major GC(主要的,全局的GC),已經(jīng)消亡的依然還是被Kill掉.

4.4.4.5 按系統(tǒng)線程分

串行收集:串行收集使用單線程處理所有垃圾回收工作, 因?yàn)闊o(wú)需多線程交互,實(shí)現(xiàn)容易,而且效率比較高。但是,其局限性也比較明顯,即無(wú)法使用多處理器的優(yōu)勢(shì),所以此收集適合單處理器機(jī)器。當(dāng)然,此收集器也可以用在小數(shù)據(jù)量(100M左右)情況下的多處理器機(jī)器上。

并行收集:并行收集使用多線程處理垃圾回收工作,因而速度快,效率高。而且理論上CPU數(shù)目越多,越能體現(xiàn)出并行收集器的優(yōu)勢(shì)。

并發(fā)收集:相對(duì)于串行收集和并行收集而言,前面兩個(gè)在進(jìn)行垃圾回收工作時(shí),需要暫停整個(gè)運(yùn)行環(huán)境(STW),而只有垃圾回收程序在運(yùn)行,因此,系統(tǒng)在垃圾回收時(shí)會(huì)有明顯的暫停,而且暫停時(shí)間會(huì)因?yàn)槎言酱蠖介L(zhǎng)。

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

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