深入淺出JVM

JVM 是 Java 程序運(yùn)行基礎(chǔ)误阻,面試時(shí)一定會遇到 JVM 相關(guān)的題。本文會先對面試中 JVM 的考察點(diǎn)進(jìn)行匯總介紹晴埂。然后對 JVM 內(nèi)存模型究反、Java 的類加載機(jī)制、常用的 GC 算法這三個(gè)知識點(diǎn)進(jìn)行詳細(xì)講解儒洛。

1. JVM知識點(diǎn)匯總

JVM 知識點(diǎn)匯總

如上圖所示精耐,JVM 知識點(diǎn)有 6 個(gè)大方向,其中琅锻,內(nèi)存模型卦停、類加載機(jī)制、GC 垃圾回收是比較重點(diǎn)的內(nèi)容浅浮。性能調(diào)優(yōu)部分偏重實(shí)際應(yīng)用,重點(diǎn)突出實(shí)踐能力捷枯。編譯器優(yōu)化和執(zhí)行模式部分偏重理論基礎(chǔ)滚秩,主要掌握知識點(diǎn)。

在開始下文前淮捆,看下你是否能夠回答以下知識點(diǎn):

  • 內(nèi)存模型:程序計(jì)數(shù)器郁油、方法區(qū)、堆攀痊、棧桐腌、本地方法棧的作用,保存哪些數(shù)據(jù)苟径;
  • 類加載:雙親委派的加載機(jī)制案站,以及常用類加載器分別加載哪種類型的類;
  • GC:堆內(nèi)存劃分棘街,分代回收的思想和依據(jù)蟆盐,以及不同垃圾回收算法實(shí)現(xiàn)的思路、適合的場景遭殉;
  • JVM調(diào)優(yōu):常用的JVM優(yōu)化參數(shù)的作用石挂,參數(shù)調(diào)優(yōu)的依據(jù),常用的JVM分析工具分析哪類問題及適用方法险污;
  • 執(zhí)行模式
  • 編譯器優(yōu)化

2. JVM 內(nèi)存模型

JVM內(nèi)存模型主要指運(yùn)行時(shí)的數(shù)據(jù)區(qū)痹愚,包括如下5個(gè)部分


JVM內(nèi)存模型
  • 棧:
    也叫方法棧,線程私有,線程在執(zhí)行每個(gè)方法時(shí)拯腮,都會創(chuàng)建一個(gè)棧楨窖式,用于存儲整個(gè)執(zhí)行過程和狀態(tài);調(diào)用方法時(shí)執(zhí)行入棧疾瓮,方法返回時(shí)執(zhí)行出棧脖镀;
  • 本地方法棧:
    和方法棧類似,不同的時(shí)狼电,執(zhí)行Java方法使用的是棧蜒灰,而執(zhí)行native方法時(shí)使用的是本地方法棧;
  • 程序計(jì)數(shù)器:
    當(dāng)前線程執(zhí)行字節(jié)碼的行號指示器肩碟,通過它可以知道下一條要執(zhí)行的指令强窖,每個(gè)線程獨(dú)占互不影響,保證線程切換后能恢復(fù)到正確的執(zhí)行位置削祈;
  • 堆:
    JVM管理的內(nèi)存中最大的一塊翅溺,存放的是對象實(shí)例;根據(jù)對象存活的周期不同髓抑,JVM把堆內(nèi)存進(jìn)行分帶管理咙崎,由垃圾收集器進(jìn)行對象的回收管理;
  • 方法區(qū):
    存儲已被虛擬機(jī)加載的類信息吨拍、常量褪猛、靜態(tài)變量等數(shù)據(jù);JDK8之前使用堆上的永久代作為方法區(qū)羹饰,而JDK8使用元空間(Meta-space)來代替伊滋;運(yùn)行時(shí)常量池是方法區(qū)的一部分,用于存放編譯期生成的各種字面量與符號引用(類被加載時(shí)觸發(fā))队秩,字符串常量池也在方法區(qū)中笑旺;
    注意:Class對象(Class.forName)是放在堆中的,而不是方法區(qū)馍资,class對象是生成的最終實(shí)例筒主,一切實(shí)例對象都放在堆中,方法區(qū)是存儲Class的基本信息鸟蟹;

3. 類加載機(jī)制

類的加載過程是指將編譯好的class類文件的字節(jié)碼讀入到內(nèi)存中物舒,將其存在方法區(qū)并創(chuàng)建對應(yīng)的Class對象;類的加載分為加載戏锹、鏈接冠胯、初始化,其中鏈接又包含驗(yàn)證锦针、準(zhǔn)備荠察、解析三步置蜀,如圖所示


JVM類的加載過程
  • 加載
    1)通過類的全限定名獲取定義此類的二進(jìn)制字節(jié)流(類加載器做的事);
    2)將字節(jié)流說代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu);
    3)在內(nèi)存中創(chuàng)建這個(gè)類的Class對象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問入口症虑;
  • 驗(yàn)證
  • 準(zhǔn)備
  • 解析
  • 初始化
    主要完成靜態(tài)代碼塊的執(zhí)行和靜態(tài)變量的賦值桃焕,只有對類主動使用時(shí)匪凉,才進(jìn)行初始化;初始化的觸發(fā)條件:
    1)創(chuàng)建類的實(shí)例
    2)訪問類的靜態(tài)變量或靜態(tài)方法
    3)Class.forName反射類
    4)某個(gè)子類被初始化
  • 卸載
    當(dāng)類對象(注意不是類的實(shí)例)不再被使用時(shí)是會被GC卸載回收的,需要注意的時(shí)JVM自帶的三個(gè)類加載器加載的類在虛擬機(jī)的整個(gè)生命周期中是不會被卸載的,只有用戶自定義的類加載器加載的類才會被卸載灼卢;

3.1 類加載器

如下圖,JVM自帶的三個(gè)類加載器分別是:BootStrap啟動類加載器来农、擴(kuò)展類加載器鞋真、應(yīng)用加載器;以及分別對應(yīng)的加載目錄沃于;


JVM類加載模式

雙親委派

java的類加載使用雙親委派模式涩咖,即一個(gè)類加載器在加載類時(shí),會遞歸的委托給父類加載器去執(zhí)行繁莹,直至頂層的啟動類加載器檩互,如果父類加載器能夠加載,則返回成功咨演,否則子加載器才會自己嘗試加載闸昨;
對于兩個(gè)不同的類加載器(自定義的、沒有繼承關(guān)系)雪标,加載同一個(gè)類零院,會導(dǎo)致兩個(gè)類不等溉跃;

雙親委派的好處

  1. 避免重復(fù)加載
  2. 防止對JDK核心類進(jìn)行篡改村刨,比如String.class由啟動類加載器加載,如果想篡改String類撰茎,那么不會生效嵌牺;

4. GC

4.1 對象已死?

判定對象的存活都與“引用”有關(guān)龄糊,有兩種方法去判斷一個(gè)對象已經(jīng)“死了”逆粹;

  • 引用計(jì)數(shù)算法
    已經(jīng)被淘汰的算法,通過添加一個(gè)引用計(jì)數(shù)器來判斷對象是否還在被引用炫惩,解決不了循環(huán)引用的問題僻弹;
  • 可達(dá)性分析算法
    從GC roots往下搜索,所走的路徑叫引用鏈他嚷,如果有有對象沒有與引用鏈相連的話蹋绽,證明對象是不可用的芭毙;GC roots包括:
    棧中引用的對象、方法區(qū)靜態(tài)引用指向的對象卸耘、方法區(qū)常量引用指向的對象退敦、本地方法棧Native引用的對象

再談引用

如果一個(gè)對象只被定義為“被引用”或者“未被引用”兩種狀態(tài),那么對于一些“食之無味蚣抗,棄之可惜”的對象就無能無力侈百;由此產(chǎn)生了四種引用類型:

  • 強(qiáng)引用:傳統(tǒng)“引用”的定義,只要被強(qiáng)引用關(guān)聯(lián)的對象永遠(yuǎn)不會被回收翰铡;
  • 軟引用:內(nèi)存不夠時(shí)被回收钝域;
  • 弱引用:比軟引用更弱,下一次垃圾收集時(shí)被回收两蟀;
  • 虛引用:最弱网梢,用來跟蹤對象被垃圾回收的活動(對象被回收時(shí)收到一個(gè)通知);

4.2 分代回收

JVM的堆內(nèi)存被分代管理赂毯,包括新生代和老年代战虏,這樣做主要是為了兼顧垃圾收集的時(shí)間開銷和內(nèi)存的空間有效利用;大部分對象很快就不再使用党涕;

  • Minor GC:對新生代的對象的收集烦感;
  • Major GC:對舊生代的對象的收集,出現(xiàn)Major GC通常會出現(xiàn)至少一次Minor GC膛堤;
  • Full GC:全局范圍的GC手趣,程序中主動調(diào)用System.gc()強(qiáng)制執(zhí)行的GC;出發(fā) Full GC的條件有:當(dāng)年輕代晉升到老年代放不下時(shí)肥荔、老年代使用率超過閾值绿渣、永久代/元空間不足時(shí)、System.gc()燕耿;
GC的執(zhí)行過程

新生代區(qū)分為3個(gè)部分:1個(gè)eden區(qū)中符、2個(gè)Survivor區(qū)(from和to,復(fù)制算法)誉帅,新創(chuàng)建的對象都會被分到Eden區(qū)淀散,這些對象經(jīng)過一次Minor GC后,如果仍然存活蚜锨,則會被分配到Survivor區(qū)档插,然后在Survivor區(qū)每熬過一次Minor GC后年齡就會增長一歲,達(dá)到一定年齡后亚再,就被移動到老年代中郭膛。

  • 詳細(xì)過程:
    GC開始前,對象只會存在于Eden區(qū)和from區(qū)氛悬,to是空的则剃,當(dāng)GC開始時(shí)凄诞,Eden中所有存活的對象都會被移動到To里,而from區(qū)域中仍然存活的對象會根據(jù)年齡來決定去向忍级,年齡達(dá)到閥值的帆谍,則移動到老年區(qū),沒有的移動到to區(qū)域轴咱,經(jīng)過gc后汛蝙,eden和from區(qū)域被清空,然后from和to對換朴肺,保證to區(qū)域?yàn)榭战呀!inor GC會一直重復(fù)這樣的過程,直到“To”區(qū)被填滿戈稿,“To”區(qū)被填滿之后西土,會將所有對象移動到年老代中。

4.3 垃圾回收算法

  • 標(biāo)記-清除
    老年代常用回收算法鞍盗;最基本的算法需了,兩個(gè)階段:先標(biāo)記要回收的對象,然后一次性回收
    缺點(diǎn):效率低般甲,清除后會產(chǎn)生大量的內(nèi)存碎片(空間碎片太多可能會導(dǎo)致當(dāng)程序需要分配大對象時(shí)無法找到連續(xù)的內(nèi)存而不得不提前觸發(fā)一次GC)肋乍;
  • 復(fù)制算法
    年輕代常用回收算法;把內(nèi)存劃分為兩等分敷存,只使用其中一個(gè)區(qū)域墓造,垃圾回收時(shí),將使用區(qū)域里存活的對象復(fù)制到另一個(gè)區(qū)域中锚烦,然后清除使用區(qū)域觅闽,類似Survivor的from和to;
    缺點(diǎn):需要兩倍內(nèi)存空間涮俄,內(nèi)存使用率較低
  • 標(biāo)記-整理
    結(jié)合了標(biāo)記-清除和復(fù)制的優(yōu)點(diǎn)
    將根節(jié)點(diǎn)開始標(biāo)記被引用的對象蛉拙,然后掃描整個(gè)堆,清除未標(biāo)記對象禽拔,然后把存活對象“壓縮”到堆的其中一塊刘离,順序排放
    缺點(diǎn):效率低

4.4 常見垃圾收集器

JVM 中提供的年輕代垃圾收集器 Serial室叉、ParNew睹栖、Parallel Scavenge 都是復(fù)制算法,而 CMS茧痕、G1野来、ZGC 都屬于標(biāo)記清除算法。

JDK版本默認(rèn)垃圾收集器

jdk1.7 默認(rèn)垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
jdk1.8 默認(rèn)垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
jdk1.9踪旷、10曼氛、11 默認(rèn)垃圾收集器G1
-XX:+PrintCommandLineFlagsjvm參數(shù)可查看默認(rèn)設(shè)置收集器類型
-XX:+PrintGCDetails亦可通過打印的GC日志的新生代豁辉、老年代名稱判斷

CMS

JDK1.7之前最主流的垃圾回收器;使用標(biāo)記-清除算法舀患,并發(fā)收集停頓谢占丁;


CMS

三色標(biāo)記算法

G1

G1取消了堆中年輕代與老年代的物理劃分聊浅,但它依然屬于分代收集器餐抢;G1算法將堆劃分為若干Region區(qū)域,一部分作為新生代一部分作為老年代低匙;


G1算法

ZGC

JDK11提供的高效垃圾回收算法旷痕,針對大堆內(nèi)存設(shè)計(jì);主要特點(diǎn):著色指針顽冶、讀屏障欺抗、并發(fā)處理、基于Region强重、內(nèi)存壓縮(整理)


ZGC算法

5. JVM調(diào)優(yōu)

5.1 編譯優(yōu)化

5.1.1 方法內(nèi)聯(lián)

調(diào)用方法要經(jīng)歷壓棧和出棧绞呈,這會帶來一定的時(shí)間和空間方面開銷,那么對于那些代碼體量不大间景,又頻繁調(diào)用的方法报强,這個(gè)時(shí)間和空間的消耗會很大;
方法內(nèi)聯(lián)的優(yōu)化就是將那些代碼體量小的方法代碼復(fù)制到發(fā)起調(diào)用的方法之中拱燃,避免真實(shí)調(diào)用秉溉;

  • -XX:CompileThreshold:設(shè)置熱點(diǎn)方法閾值,連續(xù)調(diào)用多少才能成為熱點(diǎn)方法碗誉;
  • -XX:MaxFreqInlineSize:經(jīng)常執(zhí)行的方法召嘶,內(nèi)聯(lián)優(yōu)化最大方法體,默認(rèn)JVM不會對方法體太大的方法做內(nèi)聯(lián)優(yōu)化哮缺;
  • -XX:MaxInlineSize:不經(jīng)常執(zhí)行的方法弄跌,內(nèi)聯(lián)優(yōu)化最大方法體

熱點(diǎn)方法能提高系統(tǒng)性能,提高方法內(nèi)聯(lián)的幾種方式

  • 通過JVM參數(shù)來減少熱點(diǎn)閾值或增加方法體閾值尝苇,使更多的方法進(jìn)行內(nèi)聯(lián)铛只,但這會增加內(nèi)存開銷;
  • 編程中避免一個(gè)方法中寫大量代碼糠溜,習(xí)慣使用小方法體淳玩;
  • 盡量使用final、private非竿、static關(guān)鍵字修飾方法蜕着,編碼方法因?yàn)槔^承,會需要額外的類型檢查红柱;

5.1.2 逃逸分析

逃逸分析(Escape Analysis)是判斷一個(gè)對象是否被外部方法引用或外部線程訪問的分析技術(shù)承匣,編譯器會根據(jù)逃逸分析的結(jié)果對代碼進(jìn)行優(yōu)化.

棧上分配

java對象默認(rèn)分配在堆上蓖乘,這會帶來垃圾回收的時(shí)間和空間消耗,如果一個(gè)對象只在方法類使用(未發(fā)生逃逸)韧骗,比如方法類的局部變量嘉抒,這個(gè)時(shí)候?qū)ο蠓峙涞骄€程棧上,隨著椗郾空間的回收而回收众眨,帶來性能提升;
開啟方式:-XX:+DoEscapeAnalysis(JVM默認(rèn)是開啟的)
關(guān)閉方式:-XX:-DoEscapeAnalysis

鎖消除

當(dāng)一個(gè)線程安全容器容诬,比如StringBuffer娩梨,在未發(fā)生逃逸時(shí),JIT編譯(運(yùn)行時(shí))會自動進(jìn)行Synchronized鎖消除览徒;比如如下代碼狈定,StringBuffer和StringBuilder的性能差別不大

     public static String getString(String s1, String s2) {
        StringBuffer sb = new StringBuffer();
        sb.append(s1);
        sb.append(s2);
        return sb.toString();
    }

開啟方式:-XX:+EliminateLocks

標(biāo)量替換

5.2 GC調(diào)優(yōu)

5.2.1 降低Minor GC頻率

  • 增加新生代大小

5.2.2 降低Full GC頻率

  • 減少大對象的創(chuàng)建
  • 增加堆內(nèi)存空間

5.2.3 選擇合適的GC回收器

  • 如果要求響應(yīng)速度快,選擇CMS和G1
  • 如果如果經(jīng)常產(chǎn)生大對象推薦使用G1习蓬,G1有專門存儲巨型對象分區(qū)纽什,并且會優(yōu)先對可回收空間較大的Region進(jìn)行回收(garbage first);
  • 如果物理機(jī)支持大堆內(nèi)存躲叼,可以用ZGC提高效率芦缰;

5.3 內(nèi)存分配及參數(shù)調(diào)優(yōu)

根據(jù)實(shí)際情況設(shè)置JVM的啟動參數(shù),常用的JVM優(yōu)化參數(shù):

配置參數(shù) 功能
-Xms 初始化堆大小枫慷,如:-Xms256m让蕾,一般和Xmx保持一樣
-Xmx 最大堆大小,最好設(shè)置為容器最大內(nèi)存的80%
-Xmn 新生代大小或听,推薦設(shè)置Xmx的3/8
-Xss 每個(gè)線程的堆棧大小探孝,默認(rèn)1M
-xx:+PrintGCDetail 打印GC日志
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/logs/-dump.hprof 發(fā)生OOM時(shí)自動生成Dump文件

todo ...

5.4 常用的JVM分析工具

Linux 命令 - top

查看進(jìn)程的CPU使用率、內(nèi)存使用率誉裆、系統(tǒng)負(fù)載

Linux 命令 - vmstat

jstat 命令

檢測java應(yīng)用程序?qū)崟r(shí)運(yùn)行情況顿颅,包括堆內(nèi)存信息及垃圾回收信息

jstack 命令

查看線程的堆棧信息

jmap 命令

查看堆內(nèi)存初始化配置及堆內(nèi)存的使用情況,可以把堆內(nèi)存中對象的信息足丢、對象的數(shù)量等dump到文件中粱腻,使用工具進(jìn)行分析;
jmap -dump:format=b.file=/tmp/heap.hprof 28557

jps 命令

JVM Process Status Tool斩跌,顯示當(dāng)前java進(jìn)程情況以及進(jìn)程pid绍些,可以看到啟動了多少java進(jìn)程(每個(gè)java進(jìn)程獨(dú)占一個(gè)jvm實(shí)例),類比linux的ps命令滔驶;

jconsole 命令

阿里出品 - arthas

5.5 OOM的排查

引用1
引用2

出現(xiàn)原因
  1. 內(nèi)存中加載的數(shù)據(jù)量過于龐大遇革,如一次性從數(shù)據(jù)庫取出大量數(shù)據(jù)卿闹;
  2. 死循環(huán)
  3. JVM參數(shù)內(nèi)存配置太小
  4. 根本原因:經(jīng)過一次FullGC后老年代中還是滿的
排查方式

1揭糕、通過IDE運(yùn)行跟蹤(很難找到原因)
2萝快、保存問題現(xiàn)場,發(fā)生OOM時(shí)記錄堆信息(導(dǎo)出Dump文件信息)著角,內(nèi)存溢出時(shí)jvm指令執(zhí)行bat發(fā)送郵件

解決方式
  1. 增加jvm內(nèi)存大小 -xmx -xms
  2. 觀察gc日志揪漩,配置新生代老年代大小比例。如果程序new的比較頻繁吏口,那么新生代設(shè)置大一點(diǎn)
  3. 程序優(yōu)化奄容,避免死循環(huán)。

6. 引用參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末产徊,一起剝皮案震驚了整個(gè)濱河市昂勒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌舟铜,老刑警劉巖戈盈,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異谆刨,居然都是意外死亡塘娶,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進(jìn)店門痊夭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來刁岸,“玉大人,你說我怎么就攤上這事她我『缡铮” “怎么了?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵番舆,是天一觀的道長根吁。 經(jīng)常有香客問我,道長合蔽,這世上最難降的妖魔是什么击敌? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮拴事,結(jié)果婚禮上沃斤,老公的妹妹穿的比我還像新娘。我一直安慰自己刃宵,他們只是感情好衡瓶,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著牲证,像睡著了一般哮针。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天十厢,我揣著相機(jī)與錄音等太,去河邊找鬼。 笑死蛮放,一個(gè)胖子當(dāng)著我的面吹牛缩抡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播包颁,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼瞻想,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了娩嚼?” 一聲冷哼從身側(cè)響起蘑险,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎岳悟,沒想到半個(gè)月后漠其,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡竿音,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年和屎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片春瞬。...
    茶點(diǎn)故事閱讀 39,745評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡柴信,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宽气,到底是詐尸還是另有隱情随常,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布萄涯,位于F島的核電站绪氛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏涝影。R本人自食惡果不足惜枣察,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望燃逻。 院中可真熱鬧序目,春花似錦、人聲如沸伯襟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽姆怪。三九已至叛赚,卻和暖如春澡绩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背俺附。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工肥卡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人昙读。 一個(gè)月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓召调,卻偏偏與公主長得像膨桥,于是被迫代替她去往敵國和親蛮浑。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評論 2 354

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