JVM学习笔记(1)

1、程序计数器

​ 程序计数器可以看作是当前线程所执行的字节码的行号指示器,字节码解析器工作时会通过改变这个计数器的值来读取下一条要执行的字节码指令。

特点:

  • 程序计数器是线程私有的
  • 在JVM规范中唯一一个没有规定OOM情况的区域

2、虚拟机栈

​ 每个线程运行时所需要的内存,称为虚拟机栈,虚拟机栈是用于存储与维护方法调用时所创建的栈帧,而栈帧则保存了局部变量表、操作数栈、动态链接、方法出口等信息,局部变量表则保存了各种基本的数据类型和对线的引用。

特点:

  • 虚拟机栈是线程私有的
  • 如果请求的栈深度大于虚拟机允许的深度,将抛出StackOverflowError异常
  • 如果栈在扩展时无法申请到足够的内存,将抛出OOM异常

问题辨析:

  • 垃圾回收是否涉及栈内存? – 否,方法执行完成后就会被弹出栈
  • 方法内的局部变量是否线程安全?
    • –按情况讨论,如果方法内得局部变量没有逃离方法得作用范围,那么就是线程安全的

3、本地方法栈

​ 本地方法栈与虚拟机栈所发挥的作用很类似,它们之间的区别就是虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则为虚拟机使用Native方法服务。

4、Java堆

​ Java堆是被所有线程共享的一块内存空间,它的作用就是用来存放对象实例。Java堆是垃圾收集器管理的主要区域。

​ 堆中的垃圾收集器采用的是分代收集算法,可以分为:新生代和老年代;再细致一点的有Eden区、From区、To区等。

​ 从内存分配的角度来看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer)。

5、方法区

​ 方法区和Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。在JVM规范中把方法区描述为堆的一个逻辑部分。当方法区无法满足内存分配需求时,将抛出OOM异常。

​ 在JDK1.6时方法区定义在永久代,目的是为了垃圾回收器可以像管理Java堆一样管理这部分内存。

​ 目前JDK1.8方法区规划到了本地内存(Native Memory)并且把原本放在永久代的字符串常量池移出到Java堆中。

在这里插入图片描述

6、运行时常量池

​ 运行时常量池是方法区一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用。v
​ 当类被加载之后,类信息中的常量池中的数据会进入方法区的运行时常量池中存放。

​ 可以通过 【javap -v 类文件路径 】查看类文件中的信息。

可以通过 【-XX:StringTableSize=桶个数】设置StringTable的桶个数来对项目进行优化。

​ 如果需要将有大量重复的字符串存入内存中,那么最好intern()一下,以减少堆内存中实例化对象的个数。

$ javap -v out/production/data-structures-and-arithmetic/github/jasonpang23/leetcode/Hello.class
# 类的基本信息 Start
Classfile /E:/Development/Java Projects/algorithms-and-data-structures/out/production/data-structures-and-arithmetic/github/jasonpang23/le
etcode/Hello.class
  Last modified 2020-4-2; size 574 bytes
  MD5 checksum 6edae0ad3972f35354583cfc3f325d19
  Compiled from "Hello.java"
public class github.jasonpang23.leetcode.Hello
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
# 类的基本信息 End

# 常量池 存储编译生成的各种字面量和符号引用 
Constant pool:
   #1 = Methodref          #6.#20         // java/lang/Object."<init>":()V
   #2 = Fieldref           #21.#22        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #23            // hello world
   #4 = Methodref          #24.#25        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #26            // github/jasonpang23/leetcode/Hello
   #6 = Class              #27            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lgithub/jasonpang23/leetcode/Hello;
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               args
  #17 = Utf8               [Ljava/lang/String;
  #18 = Utf8               SourceFile
  #19 = Utf8               Hello.java
  #20 = NameAndType        #7:#8          // "<init>":()V
  #21 = Class              #28            // java/lang/System
  #22 = NameAndType        #29:#30        // out:Ljava/io/PrintStream;
  #23 = Utf8               hello world
  #24 = Class              #31            // java/io/PrintStream
  #25 = NameAndType        #32:#33        // println:(Ljava/lang/String;)V
  #26 = Utf8               github/jasonpang23/leetcode/Hello
  #27 = Utf8               java/lang/Object
  #28 = Utf8               java/lang/System
  #29 = Utf8               out
  #30 = Utf8               Ljava/io/PrintStream;
  #31 = Utf8               java/io/PrintStream
  #32 = Utf8               println
  #33 = Utf8               (Ljava/lang/String;)V
  
# 类中方法的定义
{
	# 默认的构造函数
  public github.jasonpang23.leetcode.Hello();
    descriptor: ()V
    flags: ACC_PUBLIC 
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lgithub/jasonpang23/leetcode/Hello;
	
  # Main 方法
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    # 虚拟机的指令
    Code:  
      stack=2, locals=1, args_size=1
      	# 获取静态变量,通过#2到常量池中找到对应的符号引用
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        # 加载资源,通过#3到常量池中找到对应的符号引用
         3: ldc           #3                  // String hello world
         # 执行虚方法的调用,通过#4到常量池中找到对应的符号引用
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 6: 0
        line 7: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  args   [Ljava/lang/String;
}
SourceFile: "Hello.java"

注意区分常量池与运行时常量池

​ 常量池,就是一张表,虚拟机指令根据这张表找到要执行的类名、方法名、参数类型、字面量等信息。

​ 运行时常量池,当一个类被加载,它的常量池信息会被放入运行时常量池,并把里面的符号地址变为真实地址。

6_1、StringTable

String table又称为String pool,字符串常量池,其存在于堆中(jdk1.7之后改的)。最重要的一点,String table中存储的并不是String类型的对象,存储的而是指向String对象的索引,真实对象还是存储在堆中。

此外String table还存在一个hash表的特性,里面不存在相同的两个字符串。

此外String对象调用intern()方法时,会先在String table中查找是否存在与该对象相同的字符串,若存在直接返回String table中字符串的引用,若不存在则在String table中创建一个与该对象相同的字符串并返回该字符串对象的引用。

public class Hello {
    public static void main(String[] args) {
        String s1 = "a" ;
        String s2 = "b" ;
        String s3 = "ab" ;
        String s4 = s1 + s2 ;
        System.out.println(s3 == s4);
        String s6 = "vg" ;
        String s7 = new String("v") + new String("g") ;
        String s8 = s7.intern() ;
        System.out.println(s6 == s7);
        System.out.println(s8 == s6); 
    }
}
public class Hello {
    public static void main(String[] args) {
		//s1 s2 s3在堆中初始化了3个实例对象,并且StringTable将这三个对象的引用进行存储
        /**
         *          0: ldc           #2                  // String a
         *          2: astore_1
         *          3: ldc           #3                  // String b
         *          5: astore_2
         *          6: ldc           #4                  // String ab
         *          8: astore_3
         */
        String s1 = "a" ;
        String s2 = "b" ;
        String s3 = "ab" ;
        
        /**  s4 的字节码分析
         *          9: new           #5                  // class java/lang/StringBuilder
         *         12: dup
         *         13: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
         *         16: aload_1
         *         17: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
         *         20: aload_2
         *         21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
         *         24: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
         *         27: astore        4
         *         29: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
         *         32: aload_3
         *         33: aload         4
         */

        // 通过s4字节码分析可以发现s4其实是这样的:
        // new StringBuilder().append("a").append("b").toString() ;
        // append中的a 和 b在StringTable中有该对象的引用所以之间通过该引用获取对象而不需要new对象
        // 而这个toString() 方法实际是 : new String("ab") ;
        String s4 = s1 + s2 ;
        // 通过上面的分析可以很清楚的知道 s3 和 s4其实是在堆中的两个实例
        // 而 == 比较的是s3和s4的引用是否相等,所以这里输出的结果是false
        System.out.println(s3 == s4); //false
        // 通过s5字节码分析可以发现,StringTable中存在ab这个字符串对象的引用,所以也不会new对象
        // 而是从StringTable中直接返回 "ab"对象的引用



        /** s5 的字节码分析
         *         43: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
         *         46: ldc           #4                  // String ab
         *         48: astore        5
         */
        String s5 = "a" + "b"; // 编译时进行优化 s5 = "ab";
        // 那么这里 == 比较的时引用 所以结果为 true
        System.out.println(s3 == s5); // true


        /**
         *
         *         64: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
         *         67: ldc           #11                 // String vg
         *         69: astore        6
         *         71: new           #5                  // class java/lang/StringBuilder
         *         74: dup
         *         75: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
         *         78: new           #12                 // class java/lang/String
         *         81: dup
         *         82: ldc           #13                 // String v
         *         84: invokespecial #14                 // Method java/lang/String."<init>":(Ljava/lang/String;)V
         *         87: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
         *         90: new           #12                 // class java/lang/String
         *         93: dup
         *         94: ldc           #15                 // String g
         *         96: invokespecial #14                 // Method java/lang/String."<init>":(Ljava/lang/String;)V
         *         99: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
         *        102: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
         *        105: astore        7
         *
         */
        // 在堆中创建字符串实例"vg" 并且存储引用到StringTable 最后将引用返回给s6
        String s6 = "vg" ;
        // 在堆中创建字符串实例"v" 和 "g" 并且存储引用到StringTable
        // 到这里就变得和s4一样:创建StringBuilder进行字符串拼接,然后new一个 "vg"的字符串实例(并没有引用StringTable中的实例)
        // 现在堆中存在两个字符串"vg"实例
        String s7 = new String("v") + new String("g") ;
        String s8 = s7.intern() ; // 如果s7指向的字符串在StringTable中不存在,那么将s7引用的字符串实例放入到StringTable中并返回引用,否则直接返回
        System.out.println(s6 == s7); //false
        System.out.println(s8 == s6); //true
    }
}

输出结果:
false
true
false
true

类文件信息

$ javap -v out/production/data-structures-and-arithmetic/github/jasonpang23/leetcode/Hello.class
Classfile /E:/Development/Java Projects/algorithms-and-data-structures/out/production/data-structures-and-arithmetic/github/jasonpang23/le
etcode/Hello.class
  Last modified 2020-4-2; size 1389 bytes
  MD5 checksum b7505e618679aa22cc5acd1a4f8d4281
  Compiled from "Hello.java"
public class github.jasonpang23.leetcode.Hello
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #18.#45        // java/lang/Object."<init>":()V
   #2 = String             #46            // a
   #3 = String             #47            // b
   #4 = String             #48            // ab
   #5 = Class              #49            // java/lang/StringBuilder
   #6 = Methodref          #5.#45         // java/lang/StringBuilder."<init>":()V
   #7 = Methodref          #5.#50         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #8 = Methodref          #5.#51         // java/lang/StringBuilder.toString:()Ljava/lang/String;
   #9 = Fieldref           #52.#53        // java/lang/System.out:Ljava/io/PrintStream;
  #10 = Methodref          #54.#55        // java/io/PrintStream.println:(Z)V
  #11 = String             #56            // vg
  #12 = Class              #57            // java/lang/String
  #13 = String             #58            // v
  #14 = Methodref          #12.#59        // java/lang/String."<init>":(Ljava/lang/String;)V
  #15 = String             #60            // g
  #16 = Methodref          #12.#61        // java/lang/String.intern:()Ljava/lang/String;
  #17 = Class              #62            // github/jasonpang23/leetcode/Hello
  #18 = Class              #63            // java/lang/Object
  #19 = Utf8               <init>
  #20 = Utf8               ()V
  #21 = Utf8               Code
  #22 = Utf8               LineNumberTable
  #23 = Utf8               LocalVariableTable
  #24 = Utf8               this
  #25 = Utf8               Lgithub/jasonpang23/leetcode/Hello;
  #26 = Utf8               main
  #27 = Utf8               ([Ljava/lang/String;)V
  #28 = Utf8               args
  #29 = Utf8               [Ljava/lang/String;
  #30 = Utf8               s1
  #31 = Utf8               Ljava/lang/String;
  #32 = Utf8               s2
  #33 = Utf8               s3
  #34 = Utf8               s4
  #35 = Utf8               s5
  #36 = Utf8               s6
  #37 = Utf8               s7
  #38 = Utf8               s8
  #39 = Utf8               StackMapTable
  #40 = Class              #29            // "[Ljava/lang/String;"
  #41 = Class              #57            // java/lang/String
  #42 = Class              #64            // java/io/PrintStream
  #43 = Utf8               SourceFile
  #44 = Utf8               Hello.java
  #45 = NameAndType        #19:#20        // "<init>":()V
  #46 = Utf8               a
  #47 = Utf8               b
  #48 = Utf8               ab
  #49 = Utf8               java/lang/StringBuilder
  #50 = NameAndType        #65:#66        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #51 = NameAndType        #67:#68        // toString:()Ljava/lang/String;
  #52 = Class              #69            // java/lang/System
  #53 = NameAndType        #70:#71        // out:Ljava/io/PrintStream;
  #54 = Class              #64            // java/io/PrintStream
  #55 = NameAndType        #72:#73        // println:(Z)V
  #56 = Utf8               vg
  #57 = Utf8               java/lang/String
  #58 = Utf8               v
  #59 = NameAndType        #19:#74        // "<init>":(Ljava/lang/String;)V
  #60 = Utf8               g
  #61 = NameAndType        #75:#68        // intern:()Ljava/lang/String;
  #62 = Utf8               github/jasonpang23/leetcode/Hello
  #63 = Utf8               java/lang/Object
  #64 = Utf8               java/io/PrintStream
  #65 = Utf8               append
  #66 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #67 = Utf8               toString
  #68 = Utf8               ()Ljava/lang/String;
  #69 = Utf8               java/lang/System
  #70 = Utf8               out
  #71 = Utf8               Ljava/io/PrintStream;
  #72 = Utf8               println
  #73 = Utf8               (Z)V
  #74 = Utf8               (Ljava/lang/String;)V
  #75 = Utf8               intern
{
  public github.jasonpang23.leetcode.Hello();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lgithub/jasonpang23/leetcode/Hello;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=9, args_size=1
         0: ldc           #2                  // String a
         2: astore_1
         3: ldc           #3                  // String b
         5: astore_2
         6: ldc           #4                  // String ab
         8: astore_3
         9: new           #5                  // class java/lang/StringBuilder
        12: dup
        13: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
        16: aload_1
        17: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        20: aload_2
        21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        24: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        27: astore        4
        29: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
        32: aload_3
        33: aload         4
        35: if_acmpne     42
        38: iconst_1
        39: goto          43
        42: iconst_0
        43: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
        46: ldc           #4                  // String ab
        48: astore        5
        50: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
        53: aload_3
        54: aload         5
        56: if_acmpne     63
        59: iconst_1
        60: goto          64
        63: iconst_0
        64: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
        67: ldc           #11                 // String vg
        69: astore        6
        71: new           #5                  // class java/lang/StringBuilder
        74: dup
        75: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
        78: new           #12                 // class java/lang/String
        81: dup
        82: ldc           #13                 // String v
        84: invokespecial #14                 // Method java/lang/String."<init>":(Ljava/lang/String;)V
        87: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        90: new           #12                 // class java/lang/String
        93: dup
        94: ldc           #15                 // String g
        96: invokespecial #14                 // Method java/lang/String."<init>":(Ljava/lang/String;)V
        99: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       102: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
       105: astore        7
       107: aload         7
       109: invokevirtual #16                 // Method java/lang/String.intern:()Ljava/lang/String;
       112: astore        8
       114: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
       117: aload         6
       119: aload         7
       121: if_acmpne     128
       124: iconst_1
       125: goto          129
       128: iconst_0
       129: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
       132: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
       135: aload         8
       137: aload         6
       139: if_acmpne     146
       142: iconst_1
       143: goto          147
       146: iconst_0
       147: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
       150: return
      LineNumberTable:
        line 15: 0
        line 16: 3
        line 17: 6
        line 39: 9
        line 42: 29
        line 55: 46
        line 57: 50
        line 83: 67
        line 87: 71
        line 88: 107
        line 89: 114
        line 90: 132
        line 96: 150
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0     151     0  args   [Ljava/lang/String;
            3     148     1    s1   Ljava/lang/String;
            6     145     2    s2   Ljava/lang/String;
            9     142     3    s3   Ljava/lang/String;
           29     122     4    s4   Ljava/lang/String;
           50     101     5    s5   Ljava/lang/String;
           71      80     6    s6   Ljava/lang/String;
          107      44     7    s7   Ljava/lang/String;
          114      37     8    s8   Ljava/lang/String;
      StackMapTable: number_of_entries = 8
        frame_type = 255 /* full_frame */
          offset_delta = 42
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/
String ]
          stack = [ class java/io/PrintStream ]
        frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/
String ]
          stack = [ class java/io/PrintStream, int ]
        frame_type = 255 /* full_frame */
          offset_delta = 19
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/
String, class java/lang/String ]
          stack = [ class java/io/PrintStream ]
        frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/
String, class java/lang/String ]
          stack = [ class java/io/PrintStream, int ]
        frame_type = 255 /* full_frame */
          offset_delta = 63
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/
String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream ]
        frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/
String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream, int ]
        frame_type = 80 /* same_locals_1_stack_item */
          stack = [ class java/io/PrintStream ]
        frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/
String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream, int ]
}
SourceFile: "Hello.java"

7、直接内存

​ 直接内存不属于JVM运行时数据区的一部分,不受JVM内存回收管理,主要使用在NIO中作为一种本地内存和Java堆内存的中间缓存区。底层使用了Unsafe对象完成直接内存的分配回收,并且回收需要主动调用freeMemory方法

​ ByteBuffer的实现类内存,使用了Cleaner(虚引用)来监测ByteBuffer对象,一旦ByteBuffer对象被垃圾回收,那么就会由ReferenceHandle线程通过Cleaner的clean方法调用freeMeomery来释放直接内存

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值