高吞吐低延遲Java應(yīng)用的垃圾回收優(yōu)化【大牛經(jīng)驗(yàn)】

高性能應(yīng)用構(gòu)成了現(xiàn)代網(wǎng)絡(luò)的支柱。LinkedIn有許多內(nèi)部高吞吐量服務(wù)來(lái)滿足每秒數(shù)千次的用戶請(qǐng)求握童。要優(yōu)化用戶體驗(yàn)伟端,低延遲地響應(yīng)這些請(qǐng)求非常重要。

比如說(shuō)夹厌,用戶經(jīng)常用到的一個(gè)功能是了解動(dòng)態(tài)信息——不斷更新的專業(yè)活動(dòng)和內(nèi)容的列表豹爹。動(dòng)態(tài)信息在LinkedIn隨處可見(jiàn),包括公司頁(yè)面尊流,學(xué)校頁(yè)面以及最重要的主頁(yè)帅戒〉瓢铮基礎(chǔ)動(dòng)態(tài)信息數(shù)據(jù)平臺(tái)為我們的經(jīng)濟(jì)圖譜(會(huì)員崖技,公司,群組等等)中各種實(shí)體的更新建立索引钟哥,它必須高吞吐低延遲地實(shí)現(xiàn)相關(guān)的更新迎献。


圖1 LinkedIn?動(dòng)態(tài)信息

這些高吞吐低延遲的Java應(yīng)用轉(zhuǎn)變?yōu)楫a(chǎn)品,開(kāi)發(fā)人員必須確保應(yīng)用開(kāi)發(fā)周期的每個(gè)階段一致的性能腻贰。確定優(yōu)化垃圾回收(Garbage Collection,GC)的設(shè)置對(duì)達(dá)到這些指標(biāo)非常關(guān)鍵吁恍。

本文章通過(guò)一系列步驟來(lái)明確需求并優(yōu)化GC,目標(biāo)讀者是為實(shí)現(xiàn)應(yīng)用的高吞吐低延遲,對(duì)使用系統(tǒng)方法優(yōu)化GC感興趣的開(kāi)發(fā)人員冀瓦。文章中的方法來(lái)自于LinkedIn構(gòu)建下一代動(dòng)態(tài)信息數(shù)據(jù)平臺(tái)過(guò)程伴奥。這些方法包括但不局限于以下幾點(diǎn):并發(fā)標(biāo)記清除(Concurrent Mark Sweep,CMS)和G1垃圾回收器的CPU和內(nèi)存開(kāi)銷,避免長(zhǎng)期存活對(duì)象引起的持續(xù)GC周期翼闽,優(yōu)化GC線程任務(wù)分配使性能提升拾徙,以及GC停頓時(shí)間可預(yù)測(cè)所需的OS設(shè)置。

優(yōu)化GC的正確時(shí)機(jī)感局?

GC運(yùn)行隨著代碼級(jí)的優(yōu)化和工作負(fù)載而發(fā)生變化尼啡。因此在一個(gè)已實(shí)施性能優(yōu)化的接近完成的代碼庫(kù)上調(diào)整GC非常重要。但是在端到端的基本原型上進(jìn)行初步分析也很有必要询微,該原型系統(tǒng)使用存根代碼并模擬了可代表產(chǎn)品環(huán)境的工作負(fù)載崖瞭。這樣可以捕捉該架構(gòu)延遲和吞吐量的真實(shí)邊界,進(jìn)而決定是否縱向或橫向擴(kuò)展撑毛。

在下一代動(dòng)態(tài)信息數(shù)據(jù)平臺(tái)的原型階段书聚,幾乎實(shí)現(xiàn)了所有端到端的功能,并且模擬了當(dāng)前產(chǎn)品基礎(chǔ)架構(gòu)所服務(wù)的查詢負(fù)載藻雌。從中我們獲得了多種用來(lái)衡量應(yīng)用性能的工作負(fù)載特征和足夠長(zhǎng)時(shí)間運(yùn)行情況下的GC特征寺惫。

優(yōu)化GC的步驟

下面是為滿足高吞吐,低延遲需求優(yōu)化GC的總體步驟蹦疑。也包括在動(dòng)態(tài)信息數(shù)據(jù)平臺(tái)原型實(shí)施的具體細(xì)節(jié)西雀。可以看到在ParNew/CMS有最好的性能歉摧,但我們也實(shí)驗(yàn)了G1垃圾回收器艇肴。

1.理解GC基礎(chǔ)知識(shí)

理解GC工作機(jī)制非常重要,因?yàn)樾枰{(diào)整大量的參數(shù)叁温。Oracle的Hotspot JVM 內(nèi)存管理白皮書是開(kāi)始學(xué)習(xí)Hotspot JVM GC算法非常好的資料再悼。http://www.oracle.com/technetwork/java/javase/memorymanagement-whitepaper-150215.pdf

2. 仔細(xì)考量GC需求

為降低應(yīng)用性能的GC開(kāi)銷,可以優(yōu)化GC的一些特征膝但。吞吐量冲九、延遲等這些GC特征應(yīng)該長(zhǎng)時(shí)間測(cè)試運(yùn)行觀察,確保特征數(shù)據(jù)來(lái)自于應(yīng)用程序的處理對(duì)象數(shù)量發(fā)生變化的多個(gè)GC周期跟束。

·?Stop-the-world回收器回收垃圾時(shí)會(huì)暫停應(yīng)用線程莺奸。停頓的時(shí)長(zhǎng)和頻率不應(yīng)該對(duì)應(yīng)用遵守SLA產(chǎn)生不利的影響。

·?并發(fā)GC算法與應(yīng)用線程競(jìng)爭(zhēng)CPU周期冀宴。這個(gè)開(kāi)銷不應(yīng)該影響應(yīng)用吞吐量灭贷。

·?不壓縮GC算法會(huì)引起堆碎片化,導(dǎo)致full GC長(zhǎng)時(shí)間Stop-the-world停頓略贮。

·?垃圾回收工作需要占用內(nèi)存甚疟。一些GC算法產(chǎn)生更高的內(nèi)存占用仗岖。如果應(yīng)用程序需要較大的堆空間,要確保GC的內(nèi)存開(kāi)銷不能太大览妖。

·?清晰地了解GC日志和常用的JVM參數(shù)對(duì)簡(jiǎn)單調(diào)整GC運(yùn)行很有必要轧拄。GC運(yùn)行隨著代碼復(fù)雜度增長(zhǎng)或者工作特性變化而改變。

我們使用Linux OS的HotspotJava7u51讽膏,32GB堆內(nèi)存紧帕,6GB新生代(young generation)和-XX:CMSInitiatingOccupancyFraction值為70(老年代GC觸發(fā)時(shí)其空間占用率)開(kāi)始實(shí)驗(yàn)。設(shè)置較大的堆內(nèi)存用來(lái)維持長(zhǎng)期存活對(duì)象的對(duì)象緩存桅打。一旦這個(gè)緩存被填充是嗜,提升到老年代的對(duì)象比例顯著下降。

使用初始的GC配置挺尾,每三秒發(fā)生一次80ms的新生代GC停頓鹅搪,超過(guò)百分之99.9的應(yīng)用延遲100ms。這樣的GC很可能適合于SLA不太嚴(yán)格要求延遲的許多應(yīng)用遭铺。然而丽柿,我們的目標(biāo)是盡可能降低百分之99.9應(yīng)用的延遲,為此GC優(yōu)化是必不可少的魂挂。

3.理解GC指標(biāo)

優(yōu)化之前要先衡量甫题。了解GC日志的詳細(xì)細(xì)節(jié)(使用這些選項(xiàng):-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime)可以對(duì)該應(yīng)用的GC特征有總體的把握。

LinkedIn的內(nèi)部監(jiān)控和報(bào)表系統(tǒng)涂召,inGraphs和Naarad坠非,生成了各種有用的指標(biāo)可視化圖形,比如GC停頓時(shí)間百分比果正,一次停頓最大持續(xù)時(shí)間炎码,長(zhǎng)時(shí)間內(nèi)GC頻率。除了Naarad秋泳,有很多開(kāi)源工具比如gclogviewer可以從GC日志創(chuàng)建可視化圖形潦闲。

在這個(gè)階段,需要確定GC頻率和停頓時(shí)長(zhǎng)是否影響應(yīng)用滿足延遲性需求的能力迫皱。

4.降低GC頻率

在分代GC算法中歉闰,降低回收頻率可以通過(guò):(1)降低對(duì)象分配/提升率;(2)增加代空間的大小卓起。

在Hotspot JVM中和敬,新生代GC停頓時(shí)間取決于一次垃圾回收后對(duì)象的數(shù)量,而不是新生代自身的大小既绩。增加新生代大小對(duì)于應(yīng)用性能的影響需要仔細(xì)評(píng)估:

·?如果更多的數(shù)據(jù)存活而且被復(fù)制到survivor區(qū)域概龄,或者每次垃圾回收更多的數(shù)據(jù)提升到老年代还惠,增加新生代大小可能導(dǎo)致更長(zhǎng)的新生代GC停頓饲握。

·?另一方面私杜,如果每次垃圾回收后存活對(duì)象數(shù)量不會(huì)大幅增加,停頓時(shí)間可能不會(huì)延長(zhǎng)救欧。在這種情況下衰粹,減少GC頻率可能使應(yīng)用總體延遲降低和(或)吞吐量增加。

對(duì)于大部分為短期存活對(duì)象的應(yīng)用笆怠,僅僅需要控制前面所說(shuō)的參數(shù)铝耻。對(duì)于創(chuàng)建長(zhǎng)期存活對(duì)象的應(yīng)用,就需要注意蹬刷,被提升的對(duì)象可能很長(zhǎng)時(shí)間都不能被老年代GC周期回收瓢捉。如果老年代GC觸發(fā)閾值(老年代空間占用率百分比)比較低,應(yīng)用將陷入不斷的GC周期办成。設(shè)置高的GC觸發(fā)閾值可避免這一問(wèn)題泡态。

由于我們的應(yīng)用在堆中維持了長(zhǎng)期存活對(duì)象的較大緩存,將老年代GC觸發(fā)閾值設(shè)置為-XX:CMSInitiatingOccupancyFraction=92 -XX:+UseCMSInitiatingOccupancyOnly迂卢。我們也試圖增加新生代大小來(lái)減少新生代回收頻率某弦,但是并沒(méi)有采用,因?yàn)檫@增加了應(yīng)用延遲而克。

5.縮短GC停頓時(shí)間

減少新生代大小可以縮短新生代GC停頓時(shí)間靶壮,因?yàn)檫@樣被復(fù)制到survivor區(qū)域或者被提升的數(shù)據(jù)更少。但是员萍,正如前面提到的腾降,我們要觀察減少新生代大小和由此導(dǎo)致的GC頻率增加對(duì)于整體應(yīng)用吞吐量和延遲的影響。新生代GC停頓時(shí)間也依賴于tenuring threshold(提升閾值)和空間大小(見(jiàn)第6步)碎绎。

使用CMS嘗試最小化堆碎片和與之關(guān)聯(lián)的老年代垃圾回收f(shuō)ull GC停頓時(shí)間蜂莉。通過(guò)控制對(duì)象提升比例和減小-XX:CMSInitiatingOccupancyFraction的值使老年代GC在低閾值時(shí)觸發(fā)。

我們觀察到Eden區(qū)域的大部分新生代被回收混卵,幾乎沒(méi)有對(duì)象在survivor區(qū)域死亡映穗,所以我們將tenuring threshold從8降低到2(使用選項(xiàng):-XX:MaxTenuringThreshold=2),為的是縮短新生代垃圾回收消耗在數(shù)據(jù)復(fù)制上的時(shí)間。

我們也注意到新生代回收停頓時(shí)間隨著老年代空間占用率上升而延長(zhǎng)幕随。這意味著來(lái)自老年代的壓力使得對(duì)象提升花費(fèi)更多的時(shí)間蚁滋。為解決這個(gè)問(wèn)題,將總的堆內(nèi)存大小增加到40GB赘淮,減小-XX:CMSInitiatingOccupancyFraction的值到80辕录,更快地開(kāi)始老年代回收。盡管-XX:CMSInitiatingOccupancyFraction的值減小了梢卸,增大堆內(nèi)存可以避免不斷的老年代GC走诞。在本階段,我們獲得了70ms新生代回收停頓和百分之99.9延遲80ms蛤高。

6.優(yōu)化GC工作線程的任務(wù)分配

進(jìn)一步縮短新生代停頓時(shí)間蚣旱,我們決定研究?jī)?yōu)化與GC線程綁定任務(wù)的選項(xiàng)碑幅。

-XX:ParGCCardsPerStrideChunk?選項(xiàng)控制GC工作線程的任務(wù)粒度,可以幫助不使用補(bǔ)丁而獲得最佳性能塞绿,這個(gè)補(bǔ)丁用來(lái)優(yōu)化新生代垃圾回收的卡表掃描時(shí)間沟涨。有趣的是新生代GC時(shí)間隨著老年代空間的增加而延長(zhǎng)。將這個(gè)選項(xiàng)值設(shè)為32678异吻,新生代回收停頓時(shí)間降低到平均50ms裹赴。此時(shí)百分之99.9應(yīng)用延遲60ms。

也有其他選項(xiàng)將任務(wù)映射到GC線程诀浪,如果OS允許的話棋返,-XX:+BindGCTaskThreadsToCPUs選項(xiàng)綁定GC線程到個(gè)別的CPU核。-XX:+UseGCTaskAffinity使用affinity參數(shù)將任務(wù)分配給GC工作線程雷猪。然而懊昨,我們的應(yīng)用并沒(méi)有從這些選項(xiàng)發(fā)現(xiàn)任何益處。實(shí)際上春宣,一些調(diào)查顯示這些選項(xiàng)在Linux系統(tǒng)不起作用[1,2]酵颁。

7.了解GC的CPU和內(nèi)存開(kāi)銷

并發(fā)GC通常會(huì)增加CPU的使用。我們觀察了運(yùn)行良好的CMS默認(rèn)設(shè)置月帝,并發(fā)GC和G1垃圾回收器共同工作引起的CPU使用增加顯著降低了應(yīng)用的吞吐量和延遲躏惋。與CMS相比,G1可能占用了應(yīng)用更多的內(nèi)存開(kāi)銷嚷辅。對(duì)于低吞吐量的非計(jì)算密集型應(yīng)用簿姨,GC的高CPU使用率可能不需要擔(dān)心。


圖2 ParNew/CMS和G1的CPU使用百分?jǐn)?shù)%:相對(duì)來(lái)說(shuō)CPU使用率變化明顯的節(jié)點(diǎn)使用G1選項(xiàng)-XX:G1RSetUpdatingPauseTimePercent=20


圖3 ParNew/CMS和G1每秒服務(wù)的請(qǐng)求數(shù):吞吐量較低的節(jié)點(diǎn)使用G1選項(xiàng)-XX:G1RSetUpdatingPauseTimePercent=20

8.為GC優(yōu)化系統(tǒng)內(nèi)存和I/O管理

通常來(lái)說(shuō)簸搞,GC停頓發(fā)生在(1)低用戶時(shí)間扁位,高系統(tǒng)時(shí)間和高時(shí)鐘時(shí)間和(2)低用戶時(shí)間,低系統(tǒng)時(shí)間和高時(shí)鐘時(shí)間趁俊。這意味著基礎(chǔ)的進(jìn)程/OS設(shè)置存在問(wèn)題域仇。情況(1)可能說(shuō)明Linux從JVM偷頁(yè),情況(2)可能說(shuō)明清除磁盤緩存時(shí)Linux啟動(dòng)GC線程寺擂,等待I/O時(shí)線程陷入內(nèi)核暇务。在這些情況下如何設(shè)置參數(shù)可以參考該P(yáng)PT。

為避免運(yùn)行時(shí)性能損失怔软,啟動(dòng)應(yīng)用時(shí)使用JVM選項(xiàng)-XX:+AlwaysPreTouch訪問(wèn)和清零頁(yè)面垦细。設(shè)置vm.swappiness為零,除非在絕對(duì)必要時(shí)挡逼,OS不會(huì)交換頁(yè)面括改。

可能你會(huì)使用mlock將JVM頁(yè)pin在內(nèi)存中,使OS不換出頁(yè)面家坎。但是嘱能,如果系統(tǒng)用盡了所有的內(nèi)存和交換空間吝梅,OS通過(guò)kill進(jìn)程來(lái)回收內(nèi)存。通常情況下焰檩,Linux內(nèi)核會(huì)選擇高駐留內(nèi)存占用但還沒(méi)有長(zhǎng)時(shí)間運(yùn)行的進(jìn)程(OOM情況下killing進(jìn)程的工作流)憔涉。對(duì)我們而言订框,這個(gè)進(jìn)程很有可能就是我們的應(yīng)用程序析苫。一個(gè)服務(wù)具備優(yōu)雅降級(jí)(適度退化)的特點(diǎn)會(huì)更好,服務(wù)突然故障預(yù)示著不太好的可操作性——因此穿扳,我們沒(méi)有使用mlock而是vm.swappiness避免可能的交換懲罰衩侥。

LinkedIn動(dòng)態(tài)信息數(shù)據(jù)平臺(tái)的GC優(yōu)化

對(duì)于該平臺(tái)原型系統(tǒng),我們使用Hotspot JVM的兩個(gè)算法優(yōu)化垃圾回收:

·?新生代垃圾回收使用ParNew矛物,老年代垃圾回收使用CMS茫死。

·?新生代和老年代使用G1。G1用來(lái)解決堆大小為6GB或者更大時(shí)存在的低于0.5秒穩(wěn)定的履羞、可預(yù)測(cè)停頓時(shí)間的問(wèn)題峦萎。在我們用G1實(shí)驗(yàn)過(guò)程中,盡管調(diào)整了各種參數(shù)忆首,但沒(méi)有得到像ParNew/CMS一樣的GC性能或停頓時(shí)間的可預(yù)測(cè)值爱榔。我們查詢了使用G1發(fā)生內(nèi)存泄漏相關(guān)的一個(gè)bug[3],但還不能確定根本原因糙及。

使用ParNew/CMS详幽,應(yīng)用每三秒40-60ms的新生代停頓和每小時(shí)一個(gè)CMS周期。JVM選項(xiàng)如下:

1

2

3

4

5

6

7

8

// JVM sizing options

-server -Xms40g -Xmx40g -XX:MaxDirectMemorySize=4096m -XX:PermSize=256m -XX:MaxPermSize=256m??

// Young generation options

-XX:NewSize=6g -XX:MaxNewSize=6g -XX:+UseParNewGC -XX:MaxTenuringThreshold=2 -XX:SurvivorRatio=8 -XX:+UnlockDiagnosticVMOptions -XX:ParGCCardsPerStrideChunk=32768

// Old generation? options

-XX:+UseConcMarkSweepGC -XX:CMSParallelRemarkEnabled -XX:+ParallelRefProcEnabled -XX:+CMSClassUnloadingEnabled? -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly??

// Other options

-XX:+AlwaysPreTouch -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:-OmitStackTraceInFastThrow

使用這些選項(xiàng)浸锨,對(duì)于幾千次讀請(qǐng)求的吞吐量唇聘,應(yīng)用百分之99.9的延遲降低到60ms。

參考:

[1]?-XX:+BindGCTaskThreadsToCPUs似乎在Linux系統(tǒng)上不起作用柱搜,因?yàn)閔otspot/src/os/linux/vm/os_linux.cpp的distribute_processes方法在JDK7或JDK8沒(méi)有實(shí)現(xiàn)迟郎。

[2]?-XX:+UseGCTaskAffinity選項(xiàng)在JDK7和JDK8的所有平臺(tái)似乎都不起作用,因?yàn)槿蝿?wù)的affinity屬性永遠(yuǎn)被設(shè)置為sentinel_worker = (uint) -1聪蘸。源碼見(jiàn)hotspot/src/share/vm/gc_implementation/parallelScavenge/{gcTaskManager.cpp谎亩,gcTaskThread.cpp, gcTaskManager.cpp}。

[3] G1存在一些內(nèi)存泄露的bug宇姚,可能Java7u51沒(méi)有修改匈庭。這個(gè)

bug僅在Java 8修正了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末浑劳,一起剝皮案震驚了整個(gè)濱河市阱持,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌魔熏,老刑警劉巖衷咽,帶你破解...
    沈念sama閱讀 212,080評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鸽扁,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡镶骗,警方通過(guò)查閱死者的電腦和手機(jī)桶现,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)鼎姊,“玉大人骡和,你說(shuō)我怎么就攤上這事∠嗫埽” “怎么了慰于?”我有些...
    開(kāi)封第一講書人閱讀 157,630評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)唤衫。 經(jīng)常有香客問(wèn)我婆赠,道長(zhǎng),這世上最難降的妖魔是什么佳励? 我笑而不...
    開(kāi)封第一講書人閱讀 56,554評(píng)論 1 284
  • 正文 為了忘掉前任休里,我火速辦了婚禮,結(jié)果婚禮上赃承,老公的妹妹穿的比我還像新娘妙黍。我一直安慰自己,他們只是感情好楣导,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,662評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布废境。 她就那樣靜靜地躺著,像睡著了一般筒繁。 火紅的嫁衣襯著肌膚如雪噩凹。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,856評(píng)論 1 290
  • 那天毡咏,我揣著相機(jī)與錄音驮宴,去河邊找鬼。 笑死呕缭,一個(gè)胖子當(dāng)著我的面吹牛堵泽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播恢总,決...
    沈念sama閱讀 39,014評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼迎罗,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了片仿?” 一聲冷哼從身側(cè)響起纹安,我...
    開(kāi)封第一講書人閱讀 37,752評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤程癌,失蹤者是張志新(化名)和其女友劉穎赢笨,沒(méi)想到半個(gè)月后泵肄,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體楚堤,經(jīng)...
    沈念sama閱讀 44,212評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,541評(píng)論 2 327
  • 正文 我和宋清朗相戀三年塔粒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了结借。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,687評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡卒茬,死狀恐怖船老,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情扬虚,我是刑警寧澤努隙,帶...
    沈念sama閱讀 34,347評(píng)論 4 331
  • 正文 年R本政府宣布球恤,位于F島的核電站辜昵,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏咽斧。R本人自食惡果不足惜堪置,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,973評(píng)論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望张惹。 院中可真熱鬧舀锨,春花似錦、人聲如沸宛逗。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,777評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)雷激。三九已至替蔬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間屎暇,已是汗流浹背承桥。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,006評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留根悼,地道東北人凶异。 一個(gè)月前我還...
    沈念sama閱讀 46,406評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像挤巡,于是被迫代替她去往敵國(guó)和親剩彬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,576評(píng)論 2 349

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