Java虚拟机(JVM)、垃圾回收器

这篇具有很好参考价值的文章主要介绍了Java虚拟机(JVM)、垃圾回收器。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、Java简介

1、Java开发及运行版本

JRE(Java Runtime Environment,运行环境)
所有的程序都要在JRE下才能够运行。包括JVM和Java核心类库和支持文件。

JDK(Java Development Kit,开发工具包)
用来编译、调试Java程序的开发工具包。包括Java工具(javac/java/jdb等)和Java基础的类库(java API )。

JVM(Java Virtual Machine,虚拟机)
JRE的一部分,是Java的核心和基础,用来加载字节码(.class)文件、管理并分配内存、执行垃圾收集。解释自己的指令集(即字节码)并映射到本地的CPU指令集和OS的系统调用。不同的操作系统会有不同的JVM映射规则,完成跨平台性。

Java虚拟机(JVM)、垃圾回收器,java,jvm,开发语言

2、Java程序的应用版本

Java SE
标准版(桌面程序、控制台开发、嵌入式环境...),Java的基础与核心,也是JavaEE和JavaME技术的基础。
javaSE包含:面向对象、多线程、IO流、javaSwing

Java EE
在javaSE的基础上,创建了规范和框架,提供Web服务、组件模型、管理和通信API、服务器开发,如开发B/S架构软件
javaEE包含:serclet、jstl、jsp、spring、mybatis

Java ME
机顶盒、移动电话和PDA之类嵌入式消费电子设备提供的Java语言平台,包括虚拟机和一系列标准化的Java API。

Java Card
支持一些java小程序(Applets),运行在小内存设备上的平台。

二、Java的类加载

1、类加载器种类

启动类加载器(Bootstrap ClassLoader)
负责加载%JAVA_HOME%/lib目录下的 jar 包和类。它只能加载自己能够识别的类。
扩展类加载器(Extendtion ClassLoader)
它负责加载<JAVA_HOME>\lib\ext目录中的或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以使用扩展类加载器。
应用程序类加载器(Application ClassLoader)
负责加载ClassPath上所指定的类库,如果应用程序没有自定义过自己的类加载器,一般情况下这就是程序的默认类加载器。
自定义类加载器(CustomerClassLoader)
自己定义的类加载器,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader。

Java虚拟机(JVM)、垃圾回收器,java,jvm,开发语言

2、类加载步骤

1.加载(类加载器完成)

1.通过类的全限定名来获取定义此类的二进制字节流
2.将这个类字节流代表的静态存储结构转为方法区的运行时数据结构
3.在堆中生成一个代表此类的java.lang.Class对象,作为访问方法区这些数据结构的入口。

2.校验(连接阶段)

确保Class文件的字节流中包含的信息符合当前虚拟机的要求,不危害虚拟机的自身安全。
1.文件格式验证:基于字节流验证。
2.元数据验证:基于方法区的存储结构验证。
3.字节码验证:基于方法区的存储结构验证。
4.符号引用验证:基于方法区的存储结构验证。

3.准备(连接阶段)

为类变量分配内存,并将其初始化为默认值。在方法区中分配这些变量所使用的内存空间。
例1:
public static int value = 123;
此时在准备阶段过后的初始值为0而不是123;
例2:
public static final int value = 123;
此时value的值在准备阶段过后就是123。

4.解析(连接阶段)

把类型中的符号引用转换为直接引用,主要四种:
1.类或接口的解析
2.字段解析
3.类方法解析
4.接口方法解析

5.初始化

为类的静态变量赋予正确的初始值、执行类的静态代码块。
如果有父类,则先运行父类中的变量赋值语句和静态语句。

6.注意

1.类在使用时才会加载:反射触发、 new 对象。
2.加载类的子类会先加载父类。
3.static final修饰的常量属性会存到常量池中,类不会加载。

Java虚拟机(JVM)、垃圾回收器,java,jvm,开发语言

三、JVM的内存模型

Java虚拟机(JVM)、垃圾回收器,java,jvm,开发语言
运行时数据区域

1、程序计数器

存放下条指令所在单元的地址。

2、线程栈(虚拟机栈)

1.局部变量表:存储基本数据类型(int、float、byte等),如果是引用数据类型,则存储的是其在堆中的内存地址,也就是指向对象的一个指针。
2.操作数栈:操作数运算时一块临时的空间来存放操作数。
3.动态链接:将代码的符号引用转换为在方法区(运行时常量池)中的直接引用。
4.方法出口:存储了栈帧中的方法执完之后回到上一层方法的位置。

3、本地方法栈

与虚拟机栈结构一致,本地方法栈执行的是Java底层由C++编写的native方法。

4、元空间(方法区)

主要包括:常量、静态变量、类信息(对象头)、运行时常量池,操作的是直接内存。
默认情况下是off heap的(堆的一个逻辑分区)内存,大小不受jvm大小的限制,属于操作系统内存。
运行时常量池:虚拟机启动,将各个Class文件中的常量池载入到运行时常量池中(加载到内存中)。Class常量池只是一个媒介场所。

5、堆

虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。

5.1 对象结构

Java虚拟机(JVM)、垃圾回收器,java,jvm,开发语言

Java虚拟机(JVM)、垃圾回收器,java,jvm,开发语言

四、垃圾回收

Java虚拟机(JVM)、垃圾回收器,java,jvm,开发语言

1、回收策略

1.四种引用类型

强引用 
new 对象就是,发生 gc 的时候不会被回收,内存不足时抛 OOM 导致程序异常。
软引用 
用 SoftReference 类实现,内存充足时不会回收,内存不足时会回收,适合用于创建缓存。
弱引用 
用 WeakReference 类实现,只要垃圾回收机制一运行扫描到该对象时无论内存是否充足都会回收该对象。ThreadLocal 的 Key 就是弱引用。
虚引用 
要 PhantomReference 类实现,跟没有任何引用一样,任何适合都可以被回收。主要用跟踪对象被垃圾回收的状态,在 gc 时返回一个通知。

2.判定堆中对象是否为垃圾

引用计数法:在对象中添加一个引用计数器,当有地方引用这个对象时,引用计数器值+1,当引用失效时,计数器值-1。两个对象循环引用的时,各自的计数器始终不会变成0,导致无法回收,引起内存泄露。(一般不采用)。
可达性分析法:从GCRoots的对象虚拟机栈、方法区的类属性引用的对象、方法区中常量引用的对象、本地方法栈中引用的对象)的引用连来判断是否为垃圾。

Java虚拟机(JVM)、垃圾回收器,java,jvm,开发语言

3.回收算法

1.标记–清除
首先通过根节点,标记所有从根节点开始的可达对象。清除阶段,清除所有未被标记的对象。
2.标记–整理(压缩)
标记-压缩算法从根节点开始,对所有可达对象做一次标记。将所有的存活对象压缩到内存的一端,清理边界外所有的空间。
3.标记-复制
将内存区域均分为了两块(记为S0和S1),创建对象的时只用其中的一块(如S0),当S0使用完之后,将S0上面存活的对象全复制到S1上去,然后将S0全部清理掉。
4.分代收集
目前大部分JVM的采用的算法。根据对象存活周期将内存划分为几块。根据每块内存区间的特点,使用不同的回收算法,以提高垃圾回收的效率。
将Java堆划分为新生代 (Young Generation)和老年代(Old Generation)两个区域。新生代又分为Eden区、From Survivor和To Survivor。

4.分代收集算法 

Java8及之前用 分代收集算法, 会根据对象存活周期的不同将内存划分为几块, 
新生代、老年代、永久代,这样就可以根据各年代特点分别采用最适当的 GC 算法

新生代—复制算法
每次垃圾收集都能发现大批对象已死, 只有少量存活. 因此选用复制算法, 只需要付出少量存活对象的复制成本就可以完成收集.

老年代—标记整理算法
因为对象存活率高、没有额外空间对它进行分配担保, 就必须采用“标记—清理”或“标记—整理”算法来进行回收, 不必进行内存复制, 且直接腾出空闲内存.

4.三色标记法

1.三色

黑色:表示根对象,或者该对象与它引用的对象都已经被扫描过了。
灰色:该对象本身已经被标记,但是它引用的对象还没有扫描完。
白色:未被扫描的对象,如果扫描完所有对象之后,最终为白色的为不可达对象,也就是垃圾对象。

2.三色标记过程

1.初始时,全部对象都是白色的
2.GC Roots直接引用的对象变为灰色
3.从灰色集合中获取元素;将本对象直接引用的对象标记为灰色;然后将当前的对象标记为黑色。
4.重复步骤3,直到灰色的对象集合全部变为空
5.结束后,仍然被标记为白色的对象就是不可达对象,就视为垃圾对象。

Java虚拟机(JVM)、垃圾回收器,java,jvm,开发语言

2、各类回收器

垃圾收集器 工作方式 作用空间      算法   特点 适用场景
Serial
串行
新生代
复制算法
响应速度优先
适用于单CPU环境下的client模式
ParNew
并行
新生代
复制算法
响应速度优先
多CPU环境Server模式下与CMS配合使用
Parallel
并行
新生代
复制算法
吞吐量优先
适用于后台运算而不需要太多交互的场景
Serial Old
串行
老年代
标记-整理(压缩)算法
响应速度优先
适用于单CPU环境下的Client模式
Paraller Old
并行
老年代
标记-整理(压缩)算法
吞吐量优先
适用于后台运算而不需要太多交互的场景
CMS
并发
老年代
标记-清除算法
响应速度优先
适用于互联网或B/S业务
G1
并发、并行
新生代、老年代
标记-整理(压缩)算法
响应速度优先
响应速度优先

1、对象分配

1.优先分配到Eden。
2.如果对象在Eden出生并经过第一次 Minor GC后仍然存活,且能被 Survivor容纳的话,将被移动到 Survivor空间中。对象在Survivor区中每熬过一次 Minor GC,年龄就增加1岁,当到达阈值(默认为15岁,其实每个JVM、每个GC都有所不同)时,就会被晋升到老年代中。
3.大对象直接分配到老年代,尽量避免程序中出现过多的大对象。
4.长期存活的对象分配到老年代。
5.动态对象年龄判断,如果 Survivor区中相同年龄的所有对象大小的总和大于 Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代,无须等到MaxtenuringThreshold中要求的年龄。

2.GC收集

新生代收集(Minor GC/ Young GC):新生代的垃圾收集。
老年代收集(Major GC/ old GC):老年代的垃圾收集。目前,只有 CMS GC会有单独收集老年代的行为。注意,很多时候 Major GC会和Full GC混淆使用,需要具体分辨是老年代回收还是整堆回收。
混合收集(Mixed GC):收集整个新生代以及部分老年代的垃圾收集,目前,只有G1会有这种行为。
整堆收集(Full GC):收集整个java堆和方法区的垃圾收集。

1.年轻代GC(Minor GC)触发机制

(1)当年轻代空间不足时,就会触发 Minor GC,这里的年轻代满指的是Eden代满,,Survivor满不会引发GC。(每次 Minor Gc会清理年轻代的内存。)
(2)因为Java对象大多都具备朝生夕灭的特性,所以 Minor gc非常频繁,一般回收速度也比较快。这一定义既清晰又易于理解。
(3)Minor gc会引发STW,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行。

2.老年代GC(Major GC/FullGC)触发机制

(1)指发生在老年代的GC,对象从老年代消失时, Major GC”或“Fu11GC发生了。
(2)出现了 Major GC,经常会伴随至少一次的 Minor GC(但非绝对的,在Parallel Scavenge收集器的收集策略里就有直接进行 Major GC的策略选择过程)。
(3)也就是在老年代空间不足时,会先尝试触发 Minor GC。如果之后空间还不足,则触发 Major GC。
(4)Major GC的速度一般会比 Minor gc慢10倍以上,STW的时间更长。

3.触发FullGc执行的情况

(1)调用 System. gc()时,系统建议执行Fu11GC,但是不必然执行。
(2)老年代空间不足。
(3)方法区空间不足。
(4)通过 Minor GC后进入老年代的平均大小大于老年代的可用内存。
(5)由Eden区、 from区向 from区复制时,对象大小大于 To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小。

3、G1回收器

G1 作为 JDK9 之后的服务端默认收集器,不再区分年轻代和老年代进行垃圾回收。
G1 默认把堆内存分为 N 个分区,每个 1~32M。提供了四种不同区域标签 Eden、Survivor 、Old、 Humongous。H(Humongous)区可以认为是 Old 区中一种特列专门用来存储大数据的。

Java虚拟机(JVM)、垃圾回收器,java,jvm,开发语言

1.G1的运行过程

1.初始标记:标记下GC Roots能直接关联到的对象。这个阶段需要短暂停顿线程。
2.并发标记:从GC Root开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。
3.最终标记:对用户线程做一个短暂的暂停,用于处理并发标记阶段仍遗留下来的最后那少量的SATB记录(漏标对象)。
4.筛选回收:负责更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,采用复制清除算法,把有用的复制到新Region,再清理掉整个旧Region的全部空间。须暂停用户线程,由多个收集器线程并行完成。

Java虚拟机(JVM)、垃圾回收器,java,jvm,开发语言

2.G1的特点

优点:
1、支持较大的内存。
2、暂停时间可控。
3、压缩空间,避免产生内存碎片。
4、简单配置就能达到很好的性能。
5、内存模型方面:G1采用物理分区,逻辑分代,不连续的内存区域Region组成。而CMS中Eden,Survivor,Old区是连续的一整块内存。
6、G1既可以收集年轻代,也可以收集老年代,而CMS只能在老年代使用。
缺点:
1、记忆集RSet会占用比较大的内存,因此不建议在小内存下使用G1,推荐至少6G。
2、对CPU的负载可能会更大一点。
3、由于采用复制算法,GC垃圾回收过程对象复制转移会占用较多的内存,更容易出现回收失败(Allocation (Evacuation) Failure)的问题。
4、可能会降低吞吐量。
虽然 G1收集器的垃圾收集暂停时间通常要短得多,但应用程序吞吐量也往往略低一些。
相当于把一次垃圾回收的工作,分开多次进行执行(主要指老年代),单次暂停的时间虽然更加可控,但是由于每次垃圾回收的空间会更少,
总体来说垃圾回收的效率会更低,暂停的总时间会更长,所以吞吐量往往会略低一些。

3.对比CMS

CMS的老年代回收采用的是标记-清除算法
初始标记:标记GC root能直接关联的对象(短暂STW)。
并发标记:GCRootsTracing,从并发标记中的root遍历,对不可达的对象进行标记。
重新标记:修正并发标记期间因为用户操作导致标记发生表更的对象,采用的incremental update算法,会出现比较多的STW。
并发清除:由于是直接清理,不涉及对象的复制转移,所以阶段可以并发执行。

Java虚拟机(JVM)、垃圾回收器,java,jvm,开发语言

五、JVM的工具及调优

1、工具

jps:输出 JVM 中运行的进程状态信息
jstack:生成虚拟机当前时刻的线程快照
jstat:虚拟机统计信息监控工具
jinfo:实时地查看和调整虚拟机各项参数
jmap:生成虚拟机的内存转储快照,heapdump 文件
JConsole:可视化管理工具,常用

2、调优

在没有全面监控、收集性能数据之前,调优是盲目的(一般项目加个 xms 和 xmx 参数就够)。
日常分析 GC 情况优化代码比优化 GC 参数要多得多。一般如下情况不用调优的:
minor GC 单次耗时 < 50ms,频率 10 秒以上。说明年轻代 OK。
Full GC 单次耗时 < 1 秒,频率 10 分钟以上,说明年老代 OK。
GC 调优目的:GC 时间够少,GC 次数够少。

3、调优建议

-Xms5m 设置 JVM 初始堆为 5M,-Xmx5m 设置 JVM 最大堆为 5M。-Xms 跟-Xmx 值一样时可以避免每次垃圾回收完成后 JVM 重新分配内存。
-Xmn2g:设置年轻代大小为 2G,一般默认为整个堆区的 1/3 ~ 1/4。- Xss 每个线程栈空间设置。
-XX:SurvivorRatio,设置年轻代中 Eden 区与 Survivor 区的比值,默认=8,比值为 8:1:1。
-XX:+HeapDumpOnOutOfMemoryError 当 JVM 发生 OOM 时,自动生成 DUMP 文件。
-XX:PretenureSizeThreshold 当创建的对象超过指定大小时,直接把对象分配在老年代。
-XX:MaxTenuringThreshold 设定对象在 Survivor 区最大年龄阈值,超过阈值转移到老年代,默认 15。
开启 GC 日志对性能影响很小且能帮助我们定位问题,-XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:gc.log

4、JVM 参数 

参数        说明                实例
-Xms    初始堆大小,默认物理内存的1/64    -Xms512M
-Xmx    最大堆大小,默认物理内存的1/4    -Xms2G
-Xmn    新生代内存大小,官方推荐为整个堆的3/8    -Xmn512M
-Xss    线程堆栈大小,jdk1.5及之后默认1M,之前默认256k    -Xss512k
-XX:NewRatio=n    设置新生代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4    -XX:NewRatio=3
-XX:SurvivorRatio=n    年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:8,表示Eden:Survivor=8:1:1,一个Survivor区占整个年轻代的1/8    -XX:SurvivorRatio=8
-XX:PermSize=n    永久代初始值,默认为物理内存的1/64    -XX:PermSize=128M
-XX:MaxPermSize=n    永久代最大值,默认为物理内存的1/4    -XX:MaxPermSize=256M
-verbose:class    在控制台打印类加载信息
-verbose:gc    在控制台打印垃圾回收日志
-XX:+PrintGC    打印GC日志,内容简单
-XX:+PrintGCDetails    打印GC日志,内容详细
-XX:+PrintGCDateStamps    在GC日志中添加时间戳
-Xloggc:filename    指定gc日志路径    -Xloggc:/data/jvm/gc.log
-XX:+UseSerialGC    年轻代设置串行收集器Serial
-XX:+UseParallelGC    年轻代设置并行收集器Parallel Scavenge
-XX:ParallelGCThreads=n    设置Parallel Scavenge收集时使用的CPU数。并行收集线程数。    -XX:ParallelGCThreads=4
-XX:MaxGCPauseMillis=n    设置Parallel Scavenge回收的最大时间(毫秒)    -XX:MaxGCPauseMillis=100
-XX:GCTimeRatio=n    设置Parallel Scavenge垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)    -XX:GCTimeRatio=19
-XX:+UseParallelOldGC    设置老年代为并行收集器ParallelOld收集器
-XX:+UseConcMarkSweepGC    设置老年代并发收集器CMS
-XX:+CMSIncrementalMode    设置CMS收集器为增量模式,适用于单CPU情况。文章来源地址https://www.toymoban.com/news/detail-529233.html

到了这里,关于Java虚拟机(JVM)、垃圾回收器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【JVM】13. 垃圾回收器

    2023年05月29日
    浏览(39)
  • JVM之垃圾回收器

    垃圾回收常见面试题: 如何判断对象是否死亡。 简单的介绍一下强引用、软引用、弱引用、虚引用。虚引用与软引用和弱引用的区别、使用软引用能带来的好处 如何判断一个常量是废弃常量 如何判断一个类是无用的类 垃圾收集有哪些算法,各自的特点? HotSpot 为什么要分

    2024年02月14日
    浏览(43)
  • JVM基础(6)——JVM垃圾回收器简介

    作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO 联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬 学习必须往深处挖,挖的越深,基础越扎实! 阶段1、深入多线程 阶段2、深入多线程设计模式 阶段3、深入juc源码解析

    2024年01月23日
    浏览(41)
  • JVM常见的垃圾回收器(详细)

    1、Young为年轻代出发的垃圾回收器。 2、Old为老触发的垃圾回收器。 3、连线代表的是垃圾回收器的组合。CMS 和Serial Old连线代表CMS一旦不行了,Serial Old上场。 1、什么是STW? STW是Stop-The-World缩写: 是在垃圾回收算法执⾏过程当中,将JVM内存冻结丶应用程序停顿的⼀种状态。

    2024年02月08日
    浏览(35)
  • JVM垃圾回收器G1详解

    在我们应用程序所应对的业务越来越庞大、复杂,用户越来越多,没有GC就不能保证应用程序正常进行,而经常造成STW的GC又跟不上实际的需求,我们需要不断地尝试对GC进行优化。G1(Garbage-First)垃圾回收器是在Java7 update4之后引入的一个新的垃圾回收器,是当今收集器技术发

    2024年02月09日
    浏览(37)
  • JVM基础(8)——CMS垃圾回收器

    作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO 联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬 学习必须往深处挖,挖的越深,基础越扎实! 阶段1、深入多线程 阶段2、深入多线程设计模式 阶段3、深入juc源码解析

    2024年01月16日
    浏览(43)
  • 说一下 JVM 有哪些垃圾回收器?

    如果说垃圾收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。下图展示了7种作用于不同分代的收集器,其中用于回收新生代的收集器包括Serial、ParNew、Parallel Scavenge,回收老年代的收集器包括SerialOld、Parallel Old、CMS,还有用于回收整个Java堆的G1收集器

    2024年02月22日
    浏览(41)
  • JVM — JDK11垃圾回收器 ZGC

    1. ZGC介绍 ZGC(The Z Garbage Collector)是 JDK 11 中推出的一款低延迟垃圾回收器,为实现以下几个目标而诞生的垃圾回收器,停顿时间不超过 10ms,停顿时间不会因堆变大而变长,支持 8MB~4TB 级别的堆(未来支持 16TB) 2. ZGC内存和原理 2.1 ZGC内存分布 ZGC 与传统的 CMS、G1 不同、它没

    2024年02月13日
    浏览(42)
  • JVM的组件、自动垃圾回收的工作原理、分代垃圾回收过程、可用的垃圾回收器类型

    https://www.processon.com/diagraming/64c8aa11c07d99075d934311 https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html 年轻代是所有新对象被分配和老化的地方。当年轻代填满时,这会导致minor garbage collection,minor gc会回收掉很多的游离对象。游离的年轻代很快就被收集起来。一些幸存的

    2024年02月14日
    浏览(48)
  • 【jvm系列-10】深入理解jvm垃圾回收器的种类以及内部的执行原理

    JVM系列整体栏目 内容 链接地址 【一】初识虚拟机与java虚拟机 https://blog.csdn.net/zhenghuishengq/article/details/129544460 【二】jvm的类加载子系统以及jclasslib的基本使用 https://blog.csdn.net/zhenghuishengq/article/details/129610963 【三】运行时私有区域之虚拟机栈、程序计数器、本地方法栈 https

    2024年02月05日
    浏览(90)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包