java虚拟机工作原理解析

“深入剖析JVM内部机制:理解Java虚拟机的工作原理“ Java虚拟机(JVM)是Java语言的核心组件,负责将Java源代码转换为可以在计算机上运行的机器码。了解JVM的内部机制对于开发人员来说非常重要,因为它可以帮助我们更好地理解Java程序的运行行为和性能优化。本文将深入剖析JVM的内部机制,从类加载、内存管理、垃圾回收、即时编译等方面进行详细讲解。堆是存放对象实例的区域,栈是存放方法调用的区域,方法区是存放类信息和常量池的区域。在本文中,我们对JVM的内部机制进行了深入剖析,包括类加载、内存管理、垃圾回收和即时编译等方面的内容。希望本文对读者有所帮助。 阅读详情

Java虚拟机工作原理解析

标签: classloaderClassLoaderjvmJVM
117010人阅读 评论(38) 收藏 举报

一、类加载器

首先来看一下Java程序的执行过程。

                         

从这个框图很容易大体上了解java程序工作原理。首先,你写好java代码,保存到硬盘当中。然后你在命令行中输入

  1. javac YourClassName.java  
javac YourClassName.java

此时,你的java代码就被编译成字节码(.class).如果你是在Eclipse IDE或者其他开发工具中,你保存代码的时候,开发工具已经帮你完成了上述的编译工作,因此你可以在对应的目录下看到class文件。此时的class文件依然是保存在硬盘中,因此,当你在命令行中运行

  1. java YourClassName  
java YourClassName

就完成了上面红色方框中的工作。JRE的来加载器从硬盘中读取class文件,载入到系统分配给JVM的内存区域--运行数据区(Runtime Data Areas). 然后执行引擎解释或者编译类文件,转化成特定CPU的机器码,CPU执行机器码,至此完成整个过程。


接下来就重点研究一下类加载器究竟为何物?又是如何工作的?

首先看一下来加载器的一些特点,有点抽象,不过总有帮助的。


》》层级结构

类加载器被组织成一种层级结构关系,也就是父子关系。其中,Bootstrap是所有类加载器的父亲。如下图所示:

        

--Bootstrap class loader:

当运行java虚拟机时,这个类加载器被创建,它加载一些基本的Java API,包括Object这个类。需要注意的是,这个类加载器不是用java语言写的,而是用C/C++写的。

--Extension class loader:

这个加载器加载出了基本API之外的一些拓展类,包括一些与安全性能相关的类。(目前了解得不是很深,只能笼统说,待日后再详细说明)

--System Class Loader:

它加载应用程序中的类,也就是在你的classpath中配置的类。

--User-Defined Class Loader:

这是开发人员通过拓展ClassLoader类定义的自定义加载器,加载程序员定义的一些类。


》》委派模式(Delegation Mode)

仔细看上面的层次结构,当JVM加载一个类的时候,下层的加载器会将将任务委托给上一层类加载器,上一层加载检查它的命名空间中是否已经加载这个类,如果已经加载,直接使用这个类。如果没有加载,继续往上委托直到顶部。检查完了之后,按照相反的顺序进行加载,如果Bootstrap加载器找不到这个类,则往下委托,直到找到类文件。对于某个特定的类加载器来说,一个Java类只能被载入一次,也就是说在Java虚拟机中,类的完整标识是(classLoader,package,className)。一个雷可以被不同的类加载器加载。


举个具体的例子来说明,现在加入我有一个自己定义的类MyClass需要加载,如果不指定的话,一般交App(System)加载。接到任务后,System检查自己的库里是否已经有这个类,发现没有之后委托给Extension,Extension进行同样的检查,发现还是没有继续往上委托,最顶层的Boots发现自己库里也没有,于是根据它的路径(Java 核心类库,如java.lang)尝试去加载,没找到这个MaClass类,于是只好(人家看好你,交给你完成,你无能为力,只好交给别人啦)往下委托给Extension,Extension到自己的路径(JAVA_HOME/jre/lib/ext)是找,还是没找到,继续往下,此时System加载器到classpath路径寻找,找到了,于是加载到Java虚拟机。

现在假设我们将这个类放到JAVA_HOME/jre/lib/ext这个路径中去(相当于交给Extension加载器加载),按照同样的规则,最后由Extension加载器加载MyClass类,看到了吧,统一各类被两次加载到JVM,但是每次都是由不同的ClassLoader完成。


》》可见性限制

下层的加载器能够看到上层加载器中的类,反之则不行,也就是是说委托只能从下到上。


》》不允许卸载类

类加载器可以加载一个类,但是它不能卸载一个类。但是类加载器可以被删除或者被创建。


当类加载完毕之后,JVM继续按照下图完成其他工作:


框图中各个步骤简单介绍如下:

Loading:文章前面介绍的类加载,将文件系统中的Class文件载入到JVM内存(运行数据区域)

Verifying:检查载入的类文件是否符合Java规范和虚拟机规范。

Preparing:为这个类分配所需要的内存,确定这个类的属性、方法等所需的数据结构。(Prepare a data structure that assigns the memory required by classes and indicates the fields, methods, and interfaces defined in the class.)

Resolving:将该类常量池中的符号引用都改变为直接引用。(不是很理解)

Initialing:初始化类的局部变量,为静态域赋值,同时执行静态初始化块。


那么,Class Loader在加载类的时候,究竟做了些什么工作呢?

要了解这其中的细节,必须得先详细介绍一下运行数据区域。


二、运行数据区域

Runtime Data Areas:当运行一个JVM示例时,系统将分配给它一块内存区域(这块内存区域的大小可以设置的),这一内存区域由JVM自己来管理。从这一块内存中分出一块用来存储一些运行数据,例如创建的对象,传递给方法的参数,局部变量,返回值等等。分出来的这一块就称为运行数据区域。运行数据区域可以划分为6大块:Java栈、程序计数寄存器(PC寄存器)、本地方法栈(Native Method Stack)、Java堆、方法区域、运行常量池(Runtime Constant Pool)。运行常量池本应该属于方法区,但是由于其重要性,JVM规范将其独立出来说明。其中,前面3各区域(PC寄存器、Java栈、本地方法栈)是每个线程独自拥有的,后三者则是整个JVM实例中的所有线程共有的。这六大块如下图所示:


》PC计数器:

每一个线程都拥有一个PC计数器,当线程启动(start)时,PC计数器被创建,这个计数器存放当前正在被执行的字节码指令(JVM指令)的地址。

》Java栈:

同样的,Java栈也是每个线程单独拥有,线程启动时创建。这个栈中存放着一系列的栈帧(Stack Frame),JVM只能进行压入(Push)和弹出(Pop)栈帧这两种操作。每当调用一个方法时,JVM就往栈里压入一个栈帧,方法结束返回时弹出栈帧。如果方法执行时出现异常,可以调用printStackTrace等方法来查看栈的情况。栈的示意图如下:


OK。现在我们再来详细看看每一个栈帧中都放着什么东西。从示意图很容易看出,每个栈帧包含三个部分:本地变量数组,操作数栈,方法所属类的常量池引用。

》局部(本地)变量数组:

局部(本地)变量数组中,从0开始按顺序存放方法所属对象的引用、传递给方法的参数、局部变量。举个例子:

  1. public void doSomething(int a, double b, Object o) {  
  2. ...  
  3. }  
public void doSomething(int a, double b, Object o) {
...
}

这个方法的栈帧中的局部变量存储的内容分别是:

  1. 0: this  
  2. 1: a  
  3. 2,3:b  
  4. 4:0  
0: this
1: a
2,3:b
4:0

看仔细了,其中double类型的b需要两个连续的索引。取值的时候,取出的是2这个索引中的值。如果是静态方法,则数组第0个不存放this引用,而是直接存储传递的参数。

》操作数栈:

操作数栈中存放方法执行时的一些中间变量,JVM在执行方法时压入或者弹出这些变量。其实,操作数栈是方法真正工作的地方,执行方法时,局部变量数组与操作数栈根据方法定义进行数据交换。例如,执行以下代码时,操作数栈的情况如下:

  1. int a = 90;  
  2. int b = 10;  
  3. int c = a + b;  
int a = 90;
int b = 10;
int c = a + b;


注意在这个图中,操作数栈的地步是在上边,所以先压入的100位于上方。可以看出,操作数栈其实是一个数据临时存储区,存放一些中间变量,方法结束了,操作数栈也就没有啦。

》栈帧中数据引用:

除了局部变量数组和操作数栈之外,栈帧还需要一个常量池的引用。当JVM执行到需要常量池的数据时,就是通过这个引用来访问常量池的。栈帧中的数据还要负责处理方法的返回和异常。如果通过return返回,则将该方法的栈帧从Java栈中弹出。如果方法有返回值,则将返回值压入到调用该方法的方法的操作数栈中。另外,数据区中还保存中该方法可能的异常表的引用。下面的例子用来说明:

  1. class Example3C{  
  2.     public static void addAndPrint(){  
  3.         double result = addTwoTypes(1,88.88);  
  4.         System.out.println(result);  
  5.     }  
  6.     public static double addTwoTypes(int i, double d){  
  7.     return i+d;  
  8.     }  
  9.   
  10. }  
class Example3C{
    public static void addAndPrint(){
        double result = addTwoTypes(1,88.88);
        System.out.println(result);
    }
    public static double addTwoTypes(int i, double d){
    return i+d;
    }

}

执行上述代码时,Java栈如下图所示:


花些时间好好研究上图。一样需要注意的是,栈的底部在上方,先押人员addAndPrint方法的栈帧,再压入addTwoTypes方法的栈帧。上图最右边的文字说明有错误,应该是addTwoTypes的执行结果存放在addAndPrint的操作数栈中。

》》本地方法栈

当程序通过JNI(java Native Interface)调用本地方法(如C或者C++代码)时,就根据本地方法的语言类型建立相应的栈。

》》方法区域

方法区域是一个JVM实例中的所有线程共享的,当启动一个JVM实例时,方法区域被创建。它用于存运行放常量池、有关域和方法的信息、静态变量、类和方法的字节码。不同的JVM实现方式在实现方法区域的时候会有所区别。Oracle的HotSpot称之为永久区域(Permanent Area)或者永久代(Permanent Generation)。

》》运行常量池

这个区域存放类和接口的常量,除此之外,它还存放方法和域的所有引用。当一个方法或者域被引用的时候,JVM就通过运行常量池中的这些引用来查找方法和域在内存中的的实际地址。

》》堆(Heap)

堆中存放的是程序创建的对象或者实例。这个区域对JVM的性能影响很大。垃圾回收机制处理的正是这一块内存区域。

所以,类加载器加载其实就是根据编译后的Class文件,将java字节码载入JVM内存,并完成对运行数据处于的初始化工作,供执行引擎执行。


三、 执行引擎(Execution  Engine)

类加载器将字节码载入内存之后,执行引擎以Java 字节码指令为但愿,读取Java字节码。问题是,现在的java字节码机器是读不懂的,因此还必须想办法将字节码转化成平台相关的机器码。这个过程可以由解释器来执行,也可以有即时编译器(JIT Compiler)来完成。





顶
60
踩
4
 
 
Jenkins中文显示不全?三步搞定Locale插件+汉化包的正确安装姿势 本文针对Jenkins界面中文显示不全问题,提供从原理到实战的深度解决方案。文章详细解析了Locale插件与中文语言包的核心机制,并分步指导如何通过配置JVM环境、正确安装插件及执行深度排查,彻底解决汉化顽疾,实现Jenkins界面的完美中文显示。 阅读详情

相关推荐

瑞萨RA6M4平台I2C驱动移植实战:MPU6050传感器集成指南

本文详细介绍了在瑞萨RA6M4平台上进行I2C驱动移植并集成MPU6050传感器的实战指南。通过FSP配置工具添加I2C主栈,适配MPU6050官方驱动,实现传感器数据读取与调试,为嵌入式开发提供完整的解决方案。

banana的博客 109

Java 虚拟机(JVM)原理介绍

JVM 简介1、概述2、Java类的加载原理机制2.1 、Java类的加载过程2.2 、Class loader (类加载器)2.2.1 类的生命周期2.2.1.1 加载2.2.1.2 连接2.2.1.3 初始化2.2.1.4 使用2.2.1.5 卸载2.2.1.4 结束生命周期2.2.2 类加载器2.2.2 JVM类加载机制2.3 、Runtime Data Area (运行时数据区)2.3 、Execution Engine (执行引擎)2.3 、Native Interface (本地接口)2.4 、

漏墨小子的博客 3747

FPGA实战:给你的UART串口通信加上Verilog奇偶校验位(Modelsim仿真验证)

本文详细介绍了如何在FPGA中实现UART串口通信的Verilog奇偶校验功能,包括发送端校验位生成和接收端校验检测的设计与实现。通过Modelsim仿真验证,确保数据传输的可靠性,适用于嵌入式系统和数字通信领域。

weixin_34216614的博客 341

Java虚拟机的工作原理

Java虚拟机的工作原理

forlong 1261

台达PLC DIAdesigner-AX 编程笔记

1、登陆台达思图软件网站,选择Download,登陆注册好的账号,下载DIAInstaller,下载后安装(安装目录不要修改);

weixin_45823621的博客 6675

光猫桥接后网速不升反降?可能是你的路由器在“偷懒”!

本文详细分析了光猫桥接后网速不升反降的常见原因,包括路由器性能不足、MTU/MSS值设置错误等,并提供了四个关键排查步骤和路由器选购指南。通过优化PPPoE拨号设置和硬件配置,帮助用户解决网速下降问题,提升家庭网络性能。

hqmln的博客 611

Java虚拟机(JVM)的工作原理是什么?

通过掌握JVM的工作原理和性能调优技巧,我们可以更好地优化Java程序的性能,提高开发效率,为企业的业务发展提供有力的技术支持。最后,值得一提的是,虽然JVM为Java程序的执行提供了强大的支持,但Java程序本身的编写质量和算法复杂度也是影响程序性能的重要因素。因此,在进行JVM性能调优的同时,我们也需要关注Java程序本身的编写质量和算法优化等方面。新的JVM版本可能会引入新的特性和性能优化手段,因此我们需要不断地关注Java技术的最新发展,以便更好地应用JVM技术来提高我们的开发效率和程序性能。

OsbornRayleign的博客 1209

Redis架构演进:从主从、哨兵到集群,如何支撑亿级流量的秒杀场景?

本文深入探讨了分布式缓存Redis的三种核心高可用模式:主从复制、哨兵模式与集群模式。以高并发的电商秒杀业务为切入点,我们分析了每种模式的优缺点和适用场景,并最终选择Redis集群模式作为最佳解决方案。文章提供了基于Spring Boot和Spring Data Redis的完整实现代码,详细展示了如何配置和使用Redis集群来应对高并发、高可用的挑战,旨在帮助开发者在实际项目中做出正确的技术选型和架构设计。

lingRJ777的博客 723

Java虚拟机底层原理分析

Class文件中的常量池中存有大量的符号引用,字节码中的方法调用指令就是以常量池里指向方法的符号引用作为参数的,这些符号的一部分会在类加载阶段或者第一次使用的时候被转化为直接引用,这种转化被称为静态解析。因为方法的执行对应着栈帧的入栈出栈,所以将所属方法的引用存放在栈帧中。3.默认情况下,对应一个64位的服务端JVM来说,其默认的-XX:MetaspaceSize值为21MB,这就是初始的高水位线,一旦元空间的大小触及这个高水位线,就会触发Full GC并会卸载没有用的类,然后高水位线的值将会被重置。

zsh2050的专栏 1195

国科大学习资料--机器人智能控制_袁如意老师PPT后习题答案(学长精心整理)

人工神经元激活函数的作用是什么? 答:激活函数是用来加入非线性因素的,提高神经网络对模型的表达能力,解决线性模型所不能解决的问题。 人工神经网络的学习规则有哪些?各有何特征? 答:大类可以分为无监督学习算法、有监督学习算法和强化学习。其中无监督学习算法指只有输入信息,没有期望的输出信息,如Hebb学习规则,神经元连接强度的变化与两个相互连接的神经元的激活水平成正比。竞争学习法则:网络各输出单元相互竞争,最后达到只有一个最强者激活。有监督学习算法指同时提供输入信息和对应的期望输出信息,如误差学习(Delta学

无名何许人的博客 1486

Oracle EBS GL日记账批“选定以过账”状态数据修复

日记账来源:应收账款。用户操作过账以后,自动提交请求“过账:单一分类账”一直运行中,该请求执行5,6个小时甚至10多个小时无法完成,导致“标准管理器”阻塞大量请求待定,“冲突解决管理器”的待定数量不断增加。系统管理员不得不把该请求强制取消,此时,日记账批的状态处于“选定以过账”(GL_JE_BATCHES.STATUS为S),如下图 检查此日记账批有3千多条日记账题头,由于日记账比较多,日记账的科目余额计算需要大量服务器资源和执行时间比较长。

陈显平的专栏-Oracle EBS 3245

“深入解析JVM:探秘Java虚拟机的工作原理“

其中,加载阶段负责查找并加载类的二进制数据,验证阶段确保类的二进制数据符合JVM规范,准备阶段为静态变量分配内存并设置默认值,解析阶段将符号引用转换为直接引用,初始化阶段执行类的初始化代码。通过本文的介绍,我们可以看到JVM是Java程序的核心执行环境,它负责将Java字节码转换为机器码,并提供了类加载、内存管理、垃圾回收、即时编译等重要功能。JVM将内存划分为不同的区域,包括堆、栈、方法区等。摘要:本文将深入探讨Java虚拟机(JVM)的工作原理,包括类加载、内存管理、垃圾回收、即时编译等关键概念。

coder_jh的博客 848

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

“深入解析JVM:揭秘Java虚拟机的工作原理“

标题:深入解析JVM:揭秘Java虚拟机的工作原理摘要:本文将深入解析Java虚拟机(JVM)的工作原理,探讨其内部结构和运行机制。我们将介绍JVM的组成部分、类加载过程、内存管理、垃圾回收、即时编译等关键概念,并通过示例代码展示JVM的运行过程。

coder_jh的博客 832

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

“深入探索JVM:Java虚拟机的工作原理解析“

垃圾回收器的算法有多种,包括标记-清除、复制、标记-整理等。类加载器按照一定的规则进行加载,包括双亲委派模型、类加载的三个阶段(加载、链接、初始化)等。Java虚拟机(JVM)是Java语言的核心,它负责将Java源代码编译为字节码,并在运行时执行这些字节码。了解JVM的工作原理可以帮助我们编写更高效、可靠的Java代码,并及时发现和解决潜在的性能问题。摘要:本文将深入探索Java虚拟机(JVM)的工作原理,从类加载、内存管理、垃圾回收、即时编译器等方面进行详细解析,帮助读者更好地理解JVM的内部机制。

coder_jh的博客 1399

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

“深入解析JVM:探索Java虚拟机的工作原理“

我们将从JVM的整体架构开始讨论,然后逐步介绍其主要组成部分,包括类加载器、运行时数据区域、执行引擎等。类加载器是JVM的核心组件之一,它负责将Java类加载到JVM中。类加载器按照双亲委派模型工作,即先让父类加载器尝试加载类,只有当父类加载器无法加载时,才由子类加载器尝试加载。方法区用于存储类的元信息,堆用于存储对象实例,栈用于存储方法的调用栈,程序计数器用于记录当前执行的字节码指令。在以上示例代码中,JVM的执行引擎会逐条解释执行字节码指令,计算a和b的和,并将结果存储在变量c中,然后输出结果。

coder_jh的博客 691

“深入解析JVM内部机制:探索Java虚拟机的工作原理“

JVM的运行时数据区主要包括方法区、堆、栈和PC寄存器。方法区用于存储类的结构信息,堆用于存储对象实例和数组,栈用于存储方法的局部变量和方法调用的状态,PC寄存器则用于保存当前线程执行的字节码指令地址。本文深入解析了JVM的内部机制,包括架构、内存管理、垃圾回收和即时编译器等方面。通过示例代码的说明,读者可以更好地理解JVM的工作原理,并在实际开发中合理利用JVM的特性来优化程序性能。我们将从JVM的架构、内存管理、垃圾回收、即时编译器等方面进行讨论,并通过示例代码来说明各个概念的实际应用。

coder_jh的博客 851

“深入解析JVM:理解Java虚拟机的工作原理和优化技巧“

本文深入解析了JVM的工作原理和优化技巧,从JVM的基本结构开始介绍,然后介绍了内存管理优化、垃圾回收优化、JIT编译优化和并发优化等方面的技巧,并提供了相应的示例代码。我们将从JVM的基本结构开始,逐步介绍其工作原理,并提供一些实际示例代码,帮助读者更好地理解和应用JVM的优化技巧。Java虚拟机(JVM)是Java程序的运行环境,它负责将Java源代码编译为可执行的字节码并进行解释执行。标题:深入解析JVM:理解Java虚拟机的工作原理和优化技巧。一、JVM的基本结构和工作原理。

coder_jh的博客 1099

“深入解析JVM内部机制:理解Java虚拟机的工作原理“

JVM默认提供了三个类加载器:启动类加载器、扩展类加载器和应用程序类加载器。启动类加载器负责加载JVM自身需要的类,扩展类加载器负责加载JVM扩展的类库,应用程序类加载器负责加载应用程序自定义的类。本文深入解析了JVM的内部机制,包括类加载过程、运行时数据区域和垃圾回收机制。通过示例代码的说明,读者可以更好地理解JVM的内部工作机制,并在实际开发中做出相应的优化和调优。每个线程都有自己的本地方法栈空间,本地方法栈的大小由JVM在启动时设置。初始化阶段对类的静态变量进行初始化,并执行类的静态代码块。

coder_jh的博客 1073

“深入解析JVM:探索Java虚拟机的内部工作原理“

摘要:本文将深入解析Java虚拟机(JVM)的内部工作原理,包括类加载、内存管理、垃圾回收、即时编译等关键概念。在Java中,类的加载是指将类的字节码从磁盘加载到内存中,并对其进行解析和初始化的过程。JVM通过类加载器来完成类加载的任务,类加载器根据一定的规则从不同的位置加载类。JVM的内存管理包括堆内存和栈内存。即时编译是JVM对字节码进行实时编译的过程,将频繁执行的热点代码转换为本地机器码,以提高程序的执行效率。垃圾回收是JVM的一项重要功能,它负责自动回收不再使用的对象,并释放相关的内存。

coder_jh的博客 1078

“深入解析JVM内部结构与工作原理:揭秘Java虚拟机的奥秘“

运行时数据区是JVM的内存管理系统,主要包括堆、栈、方法区、程序计数器和本地方法栈等。Java虚拟机(Java Virtual Machine)是Java程序的运行环境,它负责将Java源代码编译成可执行的字节码,并在运行时提供内存管理、垃圾回收、线程管理等功能。JVM在需要类的信息时,会先通过类加载器加载字节码文件,并根据需要进行链接和初始化。摘要:本文将深入探讨Java虚拟机(JVM)的内部结构和工作原理,帮助开发者更好地理解JVM的运行机制,从而提高Java程序的性能和稳定性。这个类加载到内存中。

coder_jh的博客 525

“深入解析JVM:探索Java虚拟机的工作原理与优化技巧“

类加载是JVM的第一个阶段,它负责将Java字节码加载到内存中,并对其进行验证、准备和解析。通过合理使用数据结构和算法、减少对象的创建、避免过度同步、使用线程池和善用JVM参数等方法,可以进一步优化Java应用程序的性能。方法区用于存储类的结构信息,包括类的字段、方法、常量池等。JVM是Java应用程序的运行环境,它负责将Java字节码转换为机器码,并执行相应的指令。使用合适的数据结构和算法:选择合适的数据结构和算法可以大大提高程序的性能。减少对象的创建:对象的创建和销毁会占用大量的内存和CPU资源。

coder_jh的博客 772
上一篇: Thinking in java个人笔记1
下一篇: Thinking in java个人笔记2
DanLongChen
博客等级 码龄9年 13粉丝 54原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值