JVM实战:内存分配与回收策略

在阅读《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版) (华章原创精品) - 周志明》一书中,为增强理解,跟着实践有感。

书中第3.8.1节:对象优先在Eden分配,实验结果如下:

然后我得到的不一样的结果(开始质疑书本的正确性),后来想到可能是因为JDK版本不同,使用的垃圾收集器不同。所以后面特意指定了垃圾收集器-XX:+UseSerialGC(虚拟机运行在Client模式下的默认值,Serial+Serial Old)就与书上结果一致了。
为避免其他因素干扰,我在测试前使用了System.gz()强制JVM执行一次full gc。

private static final int _1MB = 1024 *1024;
    /**
     * VM参数:-XX:+UseSerialGC -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
     * 注意不同的收集器内存分配和回收策略不一样,本例使用-XX:+UseSerialGC,虚拟机运行在Client模式下的默认值,Serial+Serial Old。得到的结果和书上一致
     */
    public static void testAllocation() {
        System.gc();
        byte[] allocation1, allocation2, allocation3, allocation4;
        allocation1 = new byte[2 * _1MB];
        allocation2 = new byte[2 * _1MB];
        allocation3 = new byte[2 * _1MB];
        allocation4 = new byte[4 * _1MB]; // 出现一次Minor GC
    }

下面展示每一步的内存分配和回收情况,可看到初始直接执行full gc后也并不能完全回收年轻代和老年代,会有一小部分空间被占用。然后allocation1 在eden区成功分配了2MB,allocation2,allocation3也成功分配,至此eden区已有6MB。

根据我们给JVM设置的参数,堆内存20MB,不可扩展(初始内存和最大内存都为20MB),年轻代10MB(可得老年代也为10MB),eden区和survivor区的比例为8(即8:1:1,survivor分from和to),所以eden区有8MB,survivor区1M,年轻代可用空间为9MB(年轻代采用的标记-复制算法,eden+survivor中的存活对象复制到另一个survivor)。

年轻代还剩不到2MB,不足以分配allocation4,因此触发了GC(原因是内存分配失败),而目前的6MB对象都还存活,无法回收,只好通过分配担保机制提前转移到老年代,所以老年代变成了6MB,然后年轻代分配了allocation4,变为4MB。

/**
        System.gc();
//        byte[] allocation1, allocation2, allocation3, allocation4;
//        allocation1 = new byte[2 * _1MB];
//        allocation2 = new byte[2 * _1MB];
//        allocation3 = new byte[2 * _1MB];
//        allocation4 = new byte[4 * _1MB]; // 出现一次Minor GC
*/
[Full GC (System.gc()) [Tenured: 0K->724K(10240K), 0.0024904 secs] 2162K->724K(19456K), [Metaspace: 3426K->3426K(1056768K)], 0.0025292 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 246K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,   3% used [0x00000000fec00000, 0x00000000fec3d890, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 724K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,   7% used [0x00000000ff600000, 0x00000000ff6b5240, 0x00000000ff6b5400, 0x0000000100000000)
 Metaspace       used 3434K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 374K, capacity 388K, committed 512K, reserved 1048576K

/**
        System.gc();
        byte[] allocation1, allocation2, allocation3, allocation4;
        allocation1 = new byte[2 * _1MB];
//        allocation2 = new byte[2 * _1MB];
//        allocation3 = new byte[2 * _1MB];
//        allocation4 = new byte[4 * _1MB]; // 出现一次Minor GC
*/
[Full GC (System.gc()) [Tenured: 0K->713K(10240K), 0.0021630 secs] 1994K->713K(19456K), [Metaspace: 3295K->3295K(1056768K)], 0.0022069 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 2458K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  30% used [0x00000000fec00000, 0x00000000fee66858, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 713K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,   6% used [0x00000000ff600000, 0x00000000ff6b2600, 0x00000000ff6b2600, 0x0000000100000000)
 Metaspace       used 3318K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 362K, capacity 388K, committed 512K, reserved 1048576K

/**
        System.gc();
        byte[] allocation1, allocation2, allocation3, allocation4;
        allocation1 = new byte[2 * _1MB];
        allocation2 = new byte[2 * _1MB];
//        allocation3 = new byte[2 * _1MB];
//        allocation4 = new byte[4 * _1MB]; // 出现一次Minor GC
*/
[Full GC (System.gc()) [Tenured: 0K->710K(10240K), 0.0018954 secs] 1994K->710K(19456K), [Metaspace: 3221K->3221K(1056768K)], 0.0019325 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 4506K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  55% used [0x00000000fec00000, 0x00000000ff066808, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 710K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,   6% used [0x00000000ff600000, 0x00000000ff6b1920, 0x00000000ff6b1a00, 0x0000000100000000)
 Metaspace       used 3363K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 369K, capacity 388K, committed 512K, reserved 1048576K

/**
        System.gc();
        byte[] allocation1, allocation2, allocation3, allocation4;
        allocation1 = new byte[2 * _1MB];
        allocation2 = new byte[2 * _1MB];
        allocation3 = new byte[2 * _1MB];
//        allocation4 = new byte[4 * _1MB]; // 出现一次Minor GC
*/
[Full GC (System.gc()) [Tenured: 0K->710K(10240K), 0.0020691 secs] 1994K->710K(19456K), [Metaspace: 3192K->3192K(1056768K)], 0.0021076 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 6554K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  80% used [0x00000000fec00000, 0x00000000ff266818, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 710K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,   6% used [0x00000000ff600000, 0x00000000ff6b1828, 0x00000000ff6b1a00, 0x0000000100000000)
 Metaspace       used 3205K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 351K, capacity 388K, committed 512K, reserved 1048576K

/**
        System.gc();
        byte[] allocation1, allocation2, allocation3, allocation4;
        allocation1 = new byte[2 * _1MB];
        allocation2 = new byte[2 * _1MB];
        allocation3 = new byte[2 * _1MB];
        allocation4 = new byte[4 * _1MB]; // 出现一次Minor GC
*/
[Full GC (System.gc()) [Tenured: 0K->724K(10240K), 0.0027738 secs] 2162K->724K(19456K), [Metaspace: 3421K->3421K(1056768K)], 0.0028359 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [DefNew: 6471K->25K(9216K), 0.0022628 secs] 7196K->6893K(19456K), 0.0022878 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 4203K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  51% used [0x00000000fec00000, 0x00000000ff014930, 0x00000000ff400000)
  from space 1024K,   2% used [0x00000000ff500000, 0x00000000ff5064f8, 0x00000000ff600000)
  to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
 tenured generation   total 10240K, used 6868K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  67% used [0x00000000ff600000, 0x00000000ffcb5138, 0x00000000ffcb5200, 0x0000000100000000)
 Metaspace       used 3447K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 376K, capacity 388K, committed 512K, reserved 1048576K

下面再来验证下JDK8默认的垃圾收集器,首先CMD命令查看使用的是哪种收集器

java -XX:+PrintCommandLineFlags -version

-XX:InitialHeapSize=267464320 -XX:MaxHeapSize=4279429120 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)

UseParallelGC (虚拟机运行在Server模式下的默认值,Parallel Scavenge+Serial Old(PS Mark Sweep),再查看详细信息

java -XX:+PrintGCDetails -version

java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)
Heap
 PSYoungGen      total 76288K, used 2621K [0x000000076af80000, 0x0000000770480000, 0x00000007c0000000)
  eden space 65536K, 4% used [0x000000076af80000,0x000000076b20f748,0x000000076ef80000)
  from space 10752K, 0% used [0x000000076fa00000,0x000000076fa00000,0x0000000770480000)
  to   space 10752K, 0% used [0x000000076ef80000,0x000000076ef80000,0x000000076fa00000)
 ParOldGen       total 175104K, used 0K [0x00000006c0e00000, 0x00000006cb900000, 0x000000076af80000)
  object space 175104K, 0% used [0x00000006c0e00000,0x00000006c0e00000,0x00000006cb900000)
 Metaspace       used 2351K, capacity 4480K, committed 4480K, reserved 1056768K
  class space    used 254K, capacity 384K, committed 384K, reserved 1048576K

我们将对应参数改为-XX:+UseParallelGC,再来看一下结果:
allocation1, allocation2,allocation3可直接分配,与上文一致,不同的是allocation4,直接被分到了老年代(用的应该是大对象直接进入老年代的分配规则),由此可知不同收集器的实现方式是有差异的。

/**
        System.gc();
//        byte[] allocation1, allocation2, allocation3, allocation4;
//        allocation1 = new byte[2 * _1MB];
//        allocation2 = new byte[2 * _1MB];
//        allocation3 = new byte[2 * _1MB];
//        allocation4 = new byte[4 * _1MB]; // 出现一次Minor GC
*/
[GC (System.gc()) [PSYoungGen: 1994K->776K(9216K)] 1994K->784K(19456K), 0.0006403 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 776K->0K(9216K)] [ParOldGen: 8K->705K(10240K)] 784K->705K(19456K), [Metaspace: 3174K->3174K(1056768K)], 0.0031807 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
Heap
 PSYoungGen      total 9216K, used 246K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 3% used [0x00000000ff600000,0x00000000ff63d890,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 10240K, used 705K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 6% used [0x00000000fec00000,0x00000000fecb0560,0x00000000ff600000)
 Metaspace       used 3185K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 350K, capacity 388K, committed 512K, reserved 1048576K

/**
        System.gc();
        byte[] allocation1, allocation2, allocation3, allocation4;
        allocation1 = new byte[2 * _1MB];
//        allocation2 = new byte[2 * _1MB];
//        allocation3 = new byte[2 * _1MB];
//        allocation4 = new byte[4 * _1MB]; // 出现一次Minor GC
*/
[GC (System.gc()) [PSYoungGen: 2162K->808K(9216K)] 2162K->816K(19456K), 0.0015479 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 808K->0K(9216K)] [ParOldGen: 8K->716K(10240K)] 816K->716K(19456K), [Metaspace: 3339K->3339K(1056768K)], 0.0030067 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
Heap
 PSYoungGen      total 9216K, used 2457K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 30% used [0x00000000ff600000,0x00000000ff8667f8,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 10240K, used 716K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 7% used [0x00000000fec00000,0x00000000fecb33a8,0x00000000ff600000)
 Metaspace       used 3362K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 369K, capacity 388K, committed 512K, reserved 1048576K

/**
        System.gc();
        byte[] allocation1, allocation2, allocation3, allocation4;
        allocation1 = new byte[2 * _1MB];
        allocation2 = new byte[2 * _1MB];
//        allocation3 = new byte[2 * _1MB];
//        allocation4 = new byte[4 * _1MB]; // 出现一次Minor GC
*/
[GC (System.gc()) [PSYoungGen: 1994K->840K(9216K)] 1994K->848K(19456K), 0.0007088 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 840K->0K(9216K)] [ParOldGen: 8K->711K(10240K)] 848K->711K(19456K), [Metaspace: 3257K->3257K(1056768K)], 0.0030776 secs] [Times: user=0.06 sys=0.00, real=0.00 secs] 
Heap
 PSYoungGen      total 9216K, used 4506K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 55% used [0x00000000ff600000,0x00000000ffa66808,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 10240K, used 711K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 6% used [0x00000000fec00000,0x00000000fecb1ce0,0x00000000ff600000)
 Metaspace       used 3337K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 363K, capacity 388K, committed 512K, reserved 1048576K

/**
        System.gc();
        byte[] allocation1, allocation2, allocation3, allocation4;
        allocation1 = new byte[2 * _1MB];
        allocation2 = new byte[2 * _1MB];
        allocation3 = new byte[2 * _1MB];
//        allocation4 = new byte[4 * _1MB]; // 出现一次Minor GC
*/
[GC (System.gc()) [PSYoungGen: 1994K->840K(9216K)] 1994K->848K(19456K), 0.0006964 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 840K->0K(9216K)] [ParOldGen: 8K->710K(10240K)] 848K->710K(19456K), [Metaspace: 3245K->3245K(1056768K)], 0.0028236 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 PSYoungGen      total 9216K, used 6554K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 80% used [0x00000000ff600000,0x00000000ffc66878,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 10240K, used 710K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 6% used [0x00000000fec00000,0x00000000fecb1ac8,0x00000000ff600000)
 Metaspace       used 3302K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 361K, capacity 388K, committed 512K, reserved 1048576K

/**
        System.gc();
        byte[] allocation1, allocation2, allocation3, allocation4;
        allocation1 = new byte[2 * _1MB];
        allocation2 = new byte[2 * _1MB];
        allocation3 = new byte[2 * _1MB];
        allocation4 = new byte[4 * _1MB]; // 出现一次Minor GC
*/
[GC (System.gc()) [PSYoungGen: 1994K->792K(9216K)] 1994K->800K(19456K), 0.0013828 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 792K->0K(9216K)] [ParOldGen: 8K->711K(10240K)] 800K->711K(19456K), [Metaspace: 3259K->3259K(1056768K)], 0.0031560 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 PSYoungGen      total 9216K, used 6570K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 80% used [0x00000000ff600000,0x00000000ffc6a828,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 10240K, used 4807K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 46% used [0x00000000fec00000,0x00000000ff0b1d60,0x00000000ff600000)
 Metaspace       used 3447K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 376K, capacity 388K, committed 512K, reserved 1048576K

补充:

设置垃圾收集器参数
-XX:+UseSerialGC,虚拟机运行在Client模式下的默认值,Serial+Serial Old。
-XX:+UseParNewGC,ParNew+Serial Old,在JDK1.8被废弃,在JDK1.7还可以使用。
-XX:+UseConcMarkSweepGC,ParNew+CMS+Serial Old。
-XX:+UseParallelGC,虚拟机运行在Server模式下的默认值,Parallel Scavenge+Serial Old(PS Mark Sweep)。
-XX:+UseParallelOldGC,Parallel Scavenge+Parallel Old。
-XX:+UseG1GC,G1+G1。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342