jvm垃圾回收

垃圾回收.GC

1.垃圾判斷算法

1.1 引用計數(shù)算法

給對象添加一個引用計數(shù)器,當(dāng)有一個地方引用它,計數(shù)器加1呻待,當(dāng)引用失效笛粘,計數(shù)器減1趁怔,任何時刻計數(shù)器為0的對象就是不可能再被使用的。
引用計數(shù)器無法解決對象循環(huán)引用的問題(A引用B薪前,B引用A)

1.2 根搜索算法

通過一系列的成為GC Roots的點作為起始進行向下搜索润努,當(dāng)一個對象到GCRoots沒有任何引用鏈相連,則此對象時不可用的示括。

1.2.1 GC Roots
  1. 在vm棧中的引用
  2. 方法區(qū)的靜態(tài)引用
  3. jni(netive方法)中的引用
1.2.2 方法區(qū)的回收

商業(yè)化的虛擬機一般都會實現(xiàn)方法區(qū)的回收

  1. 回收內(nèi)容:
  • 廢棄常量
  • 無用類
  1. 回收滿足條件:
  • 該類的實例都被回收
  • 加載該類的類加載器被GC
  • 該類的Class對象沒有在任何地方被引用铺浇,如不能在任何地方通過反射訪問到該類。

2.GC算法

2.1 標(biāo)記-清除算法(MS)

分為標(biāo)記清除兩個階段垛膝,首先標(biāo)記所有需要回收的對象鳍侣,然后回收所有需要回收的對象。
缺點:

  • 效率較低 標(biāo)記和清除的效率都比較低吼拥。需要掃描所有對象倚聚,堆越大越慢。
  • 空間問題:回收后出現(xiàn)不連續(xù)空間扔罪,后續(xù)使用無法找到連續(xù)空間易造成再次回收秉沼。

2.2 標(biāo)記-整理算法(MC)

原理:標(biāo)記過程一樣,但后續(xù)步驟不是清除,而是令所有存活對象一端移動唬复,然后直接清理掉這端邊界以外的內(nèi)存矗积。
缺點:沒有內(nèi)存碎片,但移動(壓縮)對象更加耗費時間敞咧,

2.3 復(fù)制算法(Copying)

原理*

  • 將可用內(nèi)存劃分為兩塊棘捣,每次只使用一塊,當(dāng)半塊內(nèi)存用完了休建,僅將還存活的對象復(fù)制到另外一塊上面乍恐,原有的一塊直接清除。
  • 每次回收都是回收一半內(nèi)存测砂,也不用考慮內(nèi)存碎片問題茵烈。當(dāng)需要堆中內(nèi)存時,只需要移動堆指針砌些,按順序分配內(nèi)存呜投。
    特點
  • 這種算法是內(nèi)存會變?yōu)樵瓉硪话耄容^昂貴存璃。
  • 在對象存活率較高的時候仑荐,效率有所下降。
  • hotspot虛擬機默認eden和survivor的比例為8:1纵东,也就是每次有10%的空閑空間

2.4 分代算法(GN)

原理

  • 當(dāng)前都是采用分代收集粘招,根據(jù)對象的不同的存活周期將內(nèi)存劃分為幾塊。
  • 一般把堆內(nèi)存劃分為新生代和老年代偎球,這樣根據(jù)各個年代的不同特點采用不同的算法洒扎。
  • 新生代每次cg都有大量對象死去,所以使用復(fù)制算法甜橱,只需復(fù)制少量存活的對象逊笆。
  • 對于新生代栈戳,現(xiàn)代虛擬機使用這種算法:
    過程:eden執(zhí)行復(fù)制算法岂傲,eden區(qū)滿后會gc,還存活的對象復(fù)制到s1 survivor,當(dāng)eden又滿時子檀,eden和s1 survivor中存活的對象將被復(fù)制到s2 survivor镊掖。如此循環(huán)。
    如果對象的復(fù)制次數(shù)達到16次(默認)褂痰,該對象就會被送到老年代中亩进。
    但是也不是固定的閾值,如果達到某個年齡發(fā)現(xiàn)總大小已經(jīng)大于survivor的50%,所以要動態(tài)調(diào)整閾值(否則會導(dǎo)致survivor空間不足)缩歪,讓這些存活的對象盡快晉升归薛。
    TargetSurvivorRatio=60 ,值為一個百分比,當(dāng)對象占據(jù)survivor60時則調(diào)整閾值。
    UseConcMarkSweepGC,老年代使用cms算法主籍。
    UseParNewGC,新生代使用UseParNewGC算法习贫。
  • 老年代:存放了經(jīng)過一次或多次GC還存活的對象
    一般采用Mark-Sweep或者Mark-Compact算法
    有多重垃圾回收器可以選擇,每種垃圾回收器可以看做一種GC算法的具體實現(xiàn)千元。根據(jù)具體需求選擇合適的垃圾回收器苫昌。
  • 永久代:
    并不屬于堆,但GC也會涉及這個地方幸海。
    存放了每個Class的結(jié)構(gòu)信息祟身,包括常量池,字段描述物独,方法描述袜硫。與垃圾回收器要收集的java對象關(guān)系不大。
tops:引用類型

Hostpot將引用分為四種:

  • strong:strong為new 出來的
  • soft:內(nèi)存不夠時一定會被GC,長期不被引用也會被GC挡篓。
  • weak:一定會被GC,當(dāng)被標(biāo)記為dead時父款,會在ReferenceQueue中通知。
  • phantom:本來就沒引用瞻凤,當(dāng)從堆中釋放是會通知憨攒。
GC的時機
  1. Scavenge GC:
    觸發(fā)時機:新對象生成,Eden滿了阀参。
    理論上Eden區(qū)大多數(shù)對象會在Scavenge GC回收肝集,復(fù)制算法效率高,Scavenge GC時間短蛛壳。
  2. Full GC:
    對整個jvm整理杏瞻,包括Young,Old 和 Perm
    觸發(fā)時機:1 Old滿了,2 Perm 3.主動system.gc()衙荐。
    效率很低捞挥,盡量減少Full GC。

3.垃圾回收器的實現(xiàn)和選擇

3.1 什么是垃圾回收器

  • 分代模型:GC的愿景
  • 垃圾回收器:GC的具體實現(xiàn)
  • hotspot提供多種垃圾回收器忧吟,我們要根據(jù)具體應(yīng)用的需要采用不同的回收器砌函。
  • 沒有萬能的垃圾回收器,每一種垃圾回收器都有自己使用的場景溜族。

3.2 垃圾回收器的并行和并發(fā)

  • 并行:指多個收集器的線程同時工作讹俊,但是用戶線程處于等待狀態(tài)。
  • 并發(fā):指收集器在工作的同時煌抒,可以允許用戶線程工作仍劈。
  • 并發(fā)并不代表解決的gc停頓的問題,該停頓的步驟還是要停頓寡壮,比如標(biāo)記垃圾時贩疙。但清除垃圾時可以并發(fā)執(zhí)行讹弯。

3.3 垃圾回收器

1.Serial收集器
  • 單線程收集器,收集時會暫停所有的工作線程这溅,使用復(fù)制收集算法闸婴,虛擬機運行在clent模式下默認新生代收集器。
  • 在老年代采用標(biāo)記整理算法芍躏。
  • 可以添加jvm參數(shù)UseSerialGC配合PretenureSizeThreshold參數(shù)改變閾值邪乍,使得超過某大小的對象直接在老年代創(chuàng)建。
2.Serial old收集器
  • 單線程对竣,使用標(biāo)記整理算法庇楞,老年代收集器。
3.parnew收集器
  • Serial的多線程版本否纬,除了多個收集線程外吕晌,其余行為包括算法,stw临燃,對象分配規(guī)則睛驳。回收策略等都與Serial收集器一樣膜廊。單cpu核心場景下乏沸,此收集器不會比Serial更好。
4.parallel scavenge收集器
  • 是一個多線程收集器爪瓜,復(fù)制算法蹬跃。但他是以吞吐量最大(gc時間占總運行時間最小)為目標(biāo)的收集器铆铆。
    Parallel Scavenge(-XX:+UseParallelGC)框架下蝶缀,默認是在要觸發(fā)full GC前先執(zhí)行一次young GC,并且兩次GC之間能讓應(yīng)用程序稍微運行一小下薄货,以期降低full GC的暫停時間(因為young GC會盡量清理了young gen的死對象翁都,減少了full GC的工作量)×禄控制這個行為的VM參數(shù)是-XX:+ScavengeBeforeFullGC柄慰。這是HotSpot VM里的奇葩。
5.parallel Old
  • 老年代版本吞吐量收集器赊瞬,多線程先煎,標(biāo)記整理算法。
    MaxTenuringThreshold=5 進入到老年代的最大存活年齡
    +PrintTenuringDistribution
6.cms
  • 標(biāo)記清除算法 Concurrent Mark Sweep
  • cms是一種以最短停頓時間為目標(biāo)的收集器巧涧,使用cms并不能達到gc效率最高,但他能盡可能的降低gc時服務(wù)的停頓時間遥倦。
  1. 步驟:
  • 初始標(biāo)記:標(biāo)記gcroots關(guān)聯(lián)的引用谤绳,快速占锯,暫停用戶線程。
  • 并發(fā)標(biāo)記:gcroots關(guān)聯(lián)的引用往下接著標(biāo)記缩筛,不影響用戶線程消略。
  • 并發(fā)預(yù)先清理:
  • 并發(fā)可能失敗預(yù)清理:
  • 重新標(biāo)記:修正并發(fā)標(biāo)記時變動的引用,較快速瞎抛,暫停用戶線程艺演。
  • 并發(fā)清除:清除垃圾,不影響用戶線程桐臊。
  • 并發(fā)重置:線程重置
  1. 缺點:

    1. 依賴cpu資源胎撤,處理時間短,但處理次數(shù)多断凶。
    2. 無法處理浮動垃圾伤提,有時虛擬機會使用備案,使用Serial Old收集器來重新進行老年代的垃圾回收认烁。
    3. 空間碎片問題肿男,
  2. 空間分配擔(dān)保:
    在minor gc之前,虛擬機會檢查老年代最大可用連續(xù)空間是否大于新生代所有對象總空間却嗡。當(dāng)條件成立舶沛,在minorgc之后大量對象如果還會存活,則需要老年代進行空間分配擔(dān)保窗价,把survivor無法容納的對象直接進入老年代冠王。如果老年代判斷剩余空間不足(根據(jù)每一次回收晉升到老年代對象容量的平局值作為經(jīng)驗),則進行full gc舌镶。

4. 內(nèi)存泄漏及原因

  1. 對象定義在錯誤的范圍
  2. 異常處理不當(dāng)
  3. 集合數(shù)據(jù)管理不當(dāng)

5. 安全點理論

  1. 枚舉根節(jié)點:知道所有的根節(jié)點
    hotspot使用一組成為OopMap的數(shù)據(jù)結(jié)構(gòu)來達到這個目的柱彻。

  2. 安全點:

  • hotspot并沒有為每條指令都生成OopMap,而只是在特定位置記錄了這些信息餐胀,這些位置稱為安全點哟楷,即程序執(zhí)行時并非在所有地方都能停下來gc,而是在安全點才能gc.
  • 安全點的選定不能太少也不能太多,選定安全點的標(biāo)準是:“是否具有讓程序長時間執(zhí)行的特征”否灾,例如方法調(diào)用卖擅,循環(huán)跳轉(zhuǎn),異常跳轉(zhuǎn)墨技。
  • 對于safepoint,另一個需要考慮的是如何在gc時惩阶,讓所有的線程都跑跑到最近的安全點停下來。
    搶占式中斷:gc時先中斷扣汪,若有線程沒在安全點断楷,則恢復(fù)線程跑到安全點上。(此方式已被淘汰)
    主動式中斷:如果要gc,虛擬機設(shè)置一個標(biāo)志崭别,各個線程輪詢這個標(biāo)志冬筒,發(fā)現(xiàn)標(biāo)志為真時就中斷掛起恐锣。輪詢的地方和安全點是重合的,另外在加上創(chuàng)建對象需要分配內(nèi)存的地方舞痰。
  1. 安全區(qū)域:
  • 安全點也有解決不了的情況土榴,當(dāng)線程休眠,沒有給線程分配cpu時間响牛,線程無法響應(yīng)jvm中斷請求玷禽,jvm也不太可能等待休眠結(jié)束。此時需要安全區(qū)域解決問題呀打。
  • 線程執(zhí)行到安全區(qū)域矢赁,首先標(biāo)識自己進入了安全區(qū)域,jvm在gc時不用管在安全區(qū)域的線程聚磺。當(dāng)線程要離開安全區(qū)域是坯台,要檢查jvm是否完成了根節(jié)點枚舉或整個gc,如果完成則繼續(xù)執(zhí)行,否則等待gc完成瘫寝。

6. G1垃圾回收器

G1垃圾回收器是一種兼顧吞吐量和響應(yīng)時間的垃圾回收器
G1回收器的堆結(jié)構(gòu)和傳統(tǒng)的堆結(jié)構(gòu)有很大不同蜒蕾,傳統(tǒng)堆結(jié)構(gòu)各區(qū)域是大塊分開,G1回收器堆內(nèi)存的物理結(jié)構(gòu)是無序的焕阿,堆空間eden,survivor,old區(qū)域是交織在一起的咪啡,并且區(qū)域的分類是會變化的。

6.1 G1收集器的內(nèi)存結(jié)構(gòu)

  1. 將堆劃分為大小相等的區(qū)域(regions),每個regions都有一個分代的角色暮屡。
  2. 對每個角色的數(shù)量并沒有強制限定撤摸,也就是說對每種分代內(nèi)存的總大小,可以動態(tài)變化褒纲。
  3. G1可以高效執(zhí)行回收准夷,優(yōu)先執(zhí)行那些大量對象可回收的區(qū)域。
  4. 使用個停頓可預(yù)測模型莺掠,根據(jù)用戶設(shè)定的停頓時間,g1會選擇哪些region要清除衫嵌,一次清除多少個。
  5. g1從多個region中回收彻秆,放入一個regin中(復(fù)制算法)楔绞。
    因為會根據(jù)用戶設(shè)定的停頓時長gc,每次只收集少數(shù)的region唇兑,做到了間隔時間少酒朵,且使用copy算法,內(nèi)存碎片少扎附。

6.2 幾個概念

  1. 分區(qū):將整個堆分為大小相同的分區(qū)
  • 原有的eden,survivor,old屬于不同的塊
  • eden,survivor,old只是一種邏輯概念蔫耽。
  • 優(yōu)先回收垃圾最多區(qū)域(region)
  1. 收集集合(cset):一組可被回收的分區(qū)的集合
  • 存活的數(shù)據(jù)會被移動到另一個空閑分區(qū)
  • cset中的分區(qū)可以來自eden,survivor,old。
  1. 已記憶集合(rset):rset記錄了其他region中對象引用本region中對象的關(guān)系帕棉。
  • gc時不必掃描整個堆针肥,而是掃描rset即可饼记。即為減少掃描
  • 因為eden區(qū)gc時要全部掃描香伴,所以只需記錄老年代到新生代的引用即可慰枕。

rset的兩個作用
RSet究竟是怎么輔助GC的呢?在做YGC的時候即纲,只需要選定young generation region的RSet作為根集具帮,這些RSet記錄了old->young的跨代引用,避免了掃描整個old generation低斋。 而mixed gc的時候蜂厅,old generation中記錄了old->old的RSet,young->old的引用由掃描全部young generation region得到膊畴,這樣也不用掃描全部old generation region掘猿。所以RSet的引入大大減少了GC的工作量。

標(biāo)記出RootRegion指向O區(qū)的region唇跨,標(biāo)記這些region是為了降低并發(fā)標(biāo)記的掃描范圍稠通,因為并發(fā)標(biāo)記需要掃描GCROOT引用或間接的所有對象,而這些對象一定是在RootRegion出發(fā)指向的Region中的买猖。MIXGC中Y區(qū)本來就要全掃改橘,所以這里再按照O區(qū)過濾下,這樣就縮小了掃描范圍玉控。該階段的操作為遍歷O區(qū)region查詢Rset是否有來自RootRegion的飞主,(RootRegion是初始標(biāo)記得到的)。

  1. STAB:
  • 在并發(fā)標(biāo)記階段對快照標(biāo)記
  • stab是g1 gc在并發(fā)標(biāo)記階段使用的增量式的標(biāo)記算法高诺。
  • 并發(fā)標(biāo)記時并發(fā)多線程的碌识,但并發(fā)線程在同一時刻只掃描一個區(qū)。

拓展
card page
通常將堆空間劃分為一系列2次冪大小的卡頁.
card table:
一card張表虱而,將region劃分為小區(qū)域在這張表上筏餐。
points-out
指明我引用誰
points-into
誰引用我

6.3 gc模式

  1. YongGC:選定所有年輕代的region,通過控制年輕代region薛窥,來控制gc的時間
  • 觸發(fā)時機:eden區(qū)滿了時
  • gc步驟:
    1. 根掃描:靜態(tài)和棧引用
    2. 更新rs:處理dirty card隊列更新rs
    3. 處理rs:檢測從年輕代指向老年代的對象
    4. 對象拷貝:拷貝存活對象
    5. 處理引用隊列:軟弱虛引用處理
  1. MixedGC:選定所有年輕代regin,外加根據(jù)global concurrent marking 統(tǒng)計出的若干高收益的老年代region胖烛,在用戶指定的stw時間內(nèi)盡可能的選擇收益高的region。
  • 觸發(fā)時機:由一些參數(shù)控制诅迷,另外也控制著那些老年代regin會被選入cset
  1. global concurrent marking:全局并發(fā)標(biāo)記
    執(zhí)行過程類似于cms,但主要為MixedGC提供標(biāo)記服務(wù)佩番,并不是gc中的必須環(huán)節(jié)。
    步驟:
  • 初始標(biāo)記:標(biāo)記了從GCroot開始可以直接可達的對象罢杉。(在YongGC中進行趟畏,伴隨著YongGC)
  • 并發(fā)標(biāo)記:GCroot對heap中的對象進行標(biāo)記,標(biāo)記線程與用戶線程并發(fā)進行滩租,收集region的存活對象信息
  • 重新標(biāo)記:標(biāo)記在并發(fā)階段變化的對象
  • 清理:清空沒有存活對象的region,將region加入到free list.
  1. 三色標(biāo)記法:STAB中的算法
    對象分為三種類型
  • 黑色:根對象赋秀,或者該對象與他的子對象都被掃描過了(對象被標(biāo)記了利朵,且他的所有fieldd都被標(biāo)記完了)
  • 灰色:對象本身被掃描,但還沒掃描完該對象中的子對象(field還沒有被標(biāo)記)
  • 白色:未被掃描對象猎莲,掃描完成所有對象后最終為白色的為不可達對象绍弟,即垃圾對象。(對象沒有被標(biāo)記到)

satb步驟

  1. 開始標(biāo)記時生成快照圖
  2. 在并發(fā)標(biāo)記階段所有被改變的對象入隊(若在此階段改變引用著洼,將原有引用指向的對象變?yōu)榉前祝┱燎玻饕欠乐够厥辗抢鴮ο蟆?br> 在并發(fā)標(biāo)記階段可能產(chǎn)生漏標(biāo)記,所以對于gray灰對象一處的引用標(biāo)記為灰色身笤,對于black引用新增加的引用標(biāo)記為黑色豹悬。
  3. 可能存在浮動垃圾,將在下次護手

漏標(biāo)記情況舉例
漏標(biāo)只會發(fā)生在白色對象中液荸,需滿足以下兩個條件瞻佛。

  • 并發(fā)標(biāo)記時,應(yīng)用線程給一個黑色對象的引用賦值了白色對象
  • 并發(fā)標(biāo)記時娇钱,應(yīng)用線程刪除所有灰色對象到該白色對象的引用

掃描全部的老年代對象伤柄,是因為ygc的時候澈缺,老年代的對象全都被看做gcroots了涉波,需要在年輕代找被老年代任何對象引用的對象颠区,這些對象都不應(yīng)被清理嗡官。所以必須掃描所有老年代蹲盘,進行你說的鏈式查找的過程饮寞。而如果有了rset就知道有些old區(qū)region中根本沒有對年輕代對象的引用剖毯,直接就可以跳過這個region了旺坠。

[參考]
深入理解JVM 虛擬機
垃圾收集器|g1收集器

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疯兼,一起剝皮案震驚了整個濱河市然遏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌吧彪,老刑警劉巖待侵,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異姨裸,居然都是意外死亡秧倾,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門傀缩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來那先,“玉大人,你說我怎么就攤上這事赡艰∈鄣” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長揖闸。 經(jīng)常有香客問我揍堕,道長,這世上最難降的妖魔是什么汤纸? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任衩茸,我火速辦了婚禮,結(jié)果婚禮上蹲嚣,老公的妹妹穿的比我還像新娘递瑰。我一直安慰自己祟牲,他們只是感情好隙畜,可當(dāng)我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著说贝,像睡著了一般议惰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上乡恕,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天言询,我揣著相機與錄音,去河邊找鬼傲宜。 笑死运杭,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的函卒。 我是一名探鬼主播辆憔,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼报嵌!你這毒婦竟也來了虱咧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤锚国,失蹤者是張志新(化名)和其女友劉穎腕巡,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體血筑,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡绘沉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了豺总。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片车伞。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖园欣,靈堂內(nèi)的尸體忽然破棺而出帖世,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布日矫,位于F島的核電站赂弓,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏哪轿。R本人自食惡果不足惜盈魁,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望窃诉。 院中可真熱鬧杨耙,春花似錦、人聲如沸飘痛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽宣脉。三九已至车柠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間塑猖,已是汗流浹背竹祷。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留羊苟,地道東北人塑陵。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像蜡励,于是被迫代替她去往敵國和親令花。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,864評論 2 354