对象头markword-锁升级

锁状态的“可视化”实践
  • 通过工具查看对象的内存布局
MAVEN引入
<dependency>
  <groupId>org.openjdk.jol</groupId>
  <artifactId>jol-core</artifactId>
  <version>0.10</version>   <!— 这里不同的版本打印出来的效果不一样,0.10 和 0.16 布局的输出格式有差异 -->
</dependency>
package cn.ly.test;
import org.openjdk.jol.info.ClassLayout;
public class JavaObjLayout {
    int[] aa = new int[3];
    public static void main(String[] args) {
        A a = new A();
        B b = new B();
        C c = new C();
        int[] aa = new int[3];
        //查看对象内部信息`
        System.out.println(ClassLayout.
                parseInstance(a).toPrintable());
        System.out.println(ClassLayout.
                parseInstance(b).toPrintable());
        System.out.println(ClassLayout.
                parseInstance(c).toPrintable());
        System.out.println(ClassLayout.
                parseInstance(aa).toPrintable());
    }

    static class A {
    }

    static class B {
        private long s;
    }

    static class C {
        private int a;
        private long d;
    }
}


################################################################################################################################################################################
# WARNING: Unable to attach Serviceability Agent. You can try again with escalated privileges. Two options: a) use -Djol.tryWithSudo=true to try with sudo; b) echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
cn.ly.test.JavaObjLayout$A object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           44 c1 00 f8 (01000100 11000001 00000000 11111000) (-134168252)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


cn.ly.test.JavaObjLayout$B object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           83 c1 00 f8 (10000011 11000001 00000000 11111000) (-134168189)
     12     4        (alignment/padding gap)                  
     16     8   long B.s                                       0
Instance size: 24 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total


cn.ly.test.JavaObjLayout$C object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           c1 c1 00 f8 (11000001 11000001 00000000 11111000) (-134168127)
     12     4    int C.a                                       0
     16     8   long C.d                                       0
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total


[I object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           6d 01 00 f8 (01101101 00000001 00000000 11111000) (-134217363)
     12     4        (object header)                           03 00 00 00 (00000011 00000000 00000000 00000000) (3)
     16    12    int [I.<elements>                             N/A
     28     4        (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

################################################################################################################################################################################

注意点:
  1. jdk6默认开启偏向锁,但是是输入延时开启,也就是说,程序刚启动创建的对象是不会开启偏向锁的,几秒后后创建的对象才会开启偏向锁的
//关闭延迟开启偏向锁
-XX:BiasedLockingStartupDelay=0
//禁止偏向锁
-XX:-UseBiasedLocking
//启用偏向锁
-XX:+UseBiasedLocking
    
  1. Jol layout 结果查看需要注意顺序
对象头看锁升级过程:
一、无锁状态和偏向锁状态
  1. JVM的启动参数默认值。偏向锁相关的不做任何设置。这意味着默认会有几秒的锁偏向的延迟。
  2. 代码示例,及结果
    
public static void main(String[] args) throws InterruptedException {
    Object obj1 = new Object();
    // 由于偏向锁生效是有延迟的(JVM启动后相对时间),所以这里采用这种方式来演示延迟
    // 在偏向锁生效前先创建好一个对象 obj1 ,此时对象头默认是无锁状态:001
    TimeUnit.SECONDS.sleep(5);
    // sleep的目的是为了使"延迟的偏向锁"变得生效
    System.out.println(ClassLayout.
            parseInstance(obj1).toPrintable());

    
    // JVM偏向锁延迟生效以后,再创建对象Obj2这个时候会发现,默认对象的头标记就是偏向锁 101
    Object obj2 = new Object();
    System.out.println(ClassLayout.
            parseInstance(obj2).toPrintable());
}

################################################################################################################################################################################
# WARNING: Unable to attach Serviceability Agent. You can try again with escalated privileges. Two options: a) use -Djol.tryWithSudo=true to try with sudo; b) echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
java.lang.Object object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


java.lang.Object object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
################################################################################################################################################################################
注意结果:
二、无锁膨胀为轻量级锁
  1. JVM参数不做任何调整。即存在偏向锁的延迟.
  2. 代码示例及结果        
/**
* 轻量级锁
*/
public static void lightLock() throws InterruptedException {
    Object obj3 = new Object();
    System.out.println("无锁状态下的对象头markword标记:\n" + ClassLayout.
            parseInstance(obj3).toPrintable());
    System.out.println("------------------------------------------------------------------------------------------------------");
    Thread t1 = new Thread(()->{
        synchronized(obj3){
            System.out.println("线程t1获得锁obj3");
            System.out.println("偏向锁延迟生效时间内,无锁状态-膨胀为:轻量锁 ,其对象头markword标记:\n" + ClassLayout.
                    parseInstance(obj3).toPrintable());
        }
    });
    t1.start();
    t1.join();
}

################################################################################################################################################################################
无锁状态下的对象头markword标记:
java.lang.Object object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


------------------------------------------------------------------------------------------------------
线程t1获得锁obj3
偏向锁延迟生效时间内,无锁状态-膨胀为:轻量锁 ,其对象头markword标记:
java.lang.Object object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           08 19 60 08 (00001000 00011001 01100000 00001000) (140515592)
      4     4        (object header)                           00 70 00 00 (00000000 01110000 00000000 00000000) (28672)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
################################################################################################################################################################################

三、偏向锁和轻量锁升级
  1. JVM参数不做任何调整。即存在偏向锁的延迟.(代码中通过sleep来达到让延迟到期从而偏向生效来演示)
  2. 代码和结果如下:
/**
* 偏向锁-轻量级锁,在偏向生效以后
*/
public static void lightLockAfterBiasedLockingEffective() throws InterruptedException {
    // 由于偏向锁生效是有延迟的(JVM启动后相对时间),所以这里采用这种方式来演示延迟
    TimeUnit.SECONDS.sleep(5);
    Object obj4 = new Object();
    System.out.println("JVM偏向锁已经生效:偏向锁状态下的对象头markword标记:\n" + ClassLayout.
            parseInstance(obj4).toPrintable());
    System.out.println("------------------------------------------------------------------------------------------------------");
    Thread t2 = new Thread(()->{
        synchronized(obj4){
            System.out.println("线程t2获得锁obj4");
            System.out.println("偏向锁延迟生效后,线程t2 打印obj4 对象偏向锁状态,其对象头markword标记:\n" + ClassLayout.
                    parseInstance(obj4).toPrintable());
        }
    });
    t2.start();
    t2.join();

    TimeUnit.SECONDS.sleep(2);
    System.out.println("------------------------------------------------------------------------------------------------------");
    Thread t3 = new Thread(()->{
        synchronized(obj4){
            System.out.println("线程t3获得锁obj4");
            System.out.println("偏向锁延迟生效后,偏向锁状态-膨胀为:轻量锁 ,其对象头markword标记:\n" + ClassLayout.
                    parseInstance(obj4).toPrintable());
        }
    });
    t3.start();
    t3.join();
}
################################################################################################################################################################################
JVM偏向锁已经生效:偏向锁状态下的对象头markword标记:
java.lang.Object object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


------------------------------------------------------------------------------------------------------
t2 内存地址:31874192952,十六进制位:76bd99638,二进制位:11101101011110110011001011000111000
线程t2获得锁obj4
偏向锁延迟生效后,线程t2 打印obj4 对象偏向锁状态,其对象头markword标记:
java.lang.Object object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 48 95 c8 (00000101 01001000 10010101 11001000) (-929740795)
      4     4        (object header)                           d3 7f 00 00 (11010011 01111111 00000000 00000000) (32723)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


t2 id:14,二进制位:1110
------------------------------------------------------------------------------------------------------
线程t3获得锁obj4
偏向锁延迟生效后,偏向锁状态-膨胀为:轻量锁 ,其对象头markword标记:
java.lang.Object object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           08 b9 cc 0e (00001000 10111001 11001100 00001110) (248297736)
      4     4        (object header)                           00 70 00 00 (00000000 01110000 00000000 00000000) (28672)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

################################################################################################################################################################################

   

三、升级到重量级锁
  1. JVM参数不做任何调整。即存在偏向锁的延迟.(代码中通过sleep来达到让延迟到期从而偏向生效来演示)
  2. 代码和结果如下:
public static void weightAfterBiasedLockingEffective() throws InterruptedException {


    // 由于偏向锁生效是有延迟的(JVM启动后相对时间),所以这里采用这种方式来演示延迟
    TimeUnit.SECONDS.sleep(5);
    Object obj5 = new Object();
    System.out.println("JVM偏向锁已经生效:偏向锁状态下的对象头markword标记:\n" + ClassLayout.
            parseInstance(obj5).toPrintable());
    System.out.println("------------------------------------------------------------------------------------------------------");


    Thread t2 = new Thread(()->{
        synchronized(obj5){
            System.out.println("线程t2获得锁obj4");
            System.out.println("偏向锁延迟生效后,线程t2 打印obj5 对象偏向锁状态,其对象头markword标记:\n" + ClassLayout.
                    parseInstance(obj5).toPrintable());
            long tid = Thread.currentThread().getId();
            System.out.println("t2 id:"+ tid + ",二进制位:" + Long.toBinaryString(tid));
        }
    });
    t2.start();
    long t2Addr =  VM.current().addressOf(t2);
    System.out.println("t2 内存地址:"+ t2Addr + ",十六进制位:" + Long.toHexString(t2Addr)+ ",二进制位:" + Long.toBinaryString(t2Addr));
    t2.join();
    System.out.println("------------------------------------------------------------------------------------------------------");




    TimeUnit.MILLISECONDS.sleep(500);
    Thread t3 = new Thread(()->{
        synchronized(obj5){
            System.out.println("[线程t3] 获得锁obj5");
            System.out.println("偏向锁延迟生效后,偏向锁状态-膨胀为:??? ,其对象头markword标记:\n" + ClassLayout.
                    parseInstance(obj5).toPrintable());
        }
    });
    t3.start();


    Thread t4 = new Thread(()->{
        synchronized(obj5){
            System.out.println("[线程t4] 获得锁obj5");
            System.out.println("偏向锁延迟生效后,偏向锁状态-膨胀为:??? ,其对象头markword标记:\n" + ClassLayout.
                    parseInstance(obj5).toPrintable());
        }
    });
    t4.start();

}
备注:升级重量级锁的过程稍微复杂一些,会根据线程的调度情况和JVM运行时的情况不一样产生三种不一样的结果。
以下是多次跑代码后得到的3种不一样的结果
##########################################################################情况一######################################################################################################
JVM偏向锁已经生效:偏向锁状态下的对象头markword标记:
java.lang.Object object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


------------------------------------------------------------------------------------------------------
t2 内存地址:31874198304,十六进制位:76bd9ab20,二进制位:11101101011110110011010101100100000
线程t2获得锁obj4
偏向锁延迟生效后,线程t2 打印obj5 对象偏向锁状态,其对象头markword标记:
java.lang.Object object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 c8 aa dd (00000101 11001000 10101010 11011101) (-576010235)
      4     4        (object header)                           c4 7f 00 00 (11000100 01111111 00000000 00000000) (32708)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


t2 id:14,二进制位:1110
------------------------------------------------------------------------------------------------------
[线程t3] 获得锁obj5
偏向锁延迟生效后,偏向锁状态-膨胀为:重量级锁 ,其对象头markword标记:
java.lang.Object object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           7a 0c 81 dc (01111010 00001100 10000001 11011100) (-595522438)
      4     4        (object header)                           c4 7f 00 00 (11000100 01111111 00000000 00000000) (32708)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


[线程t4] 获得锁obj5
偏向锁延迟生效后,偏向锁状态-膨胀为:重量级锁 ,其对象头markword标记:
java.lang.Object object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           7a 0c 81 dc (01111010 00001100 10000001 11011100) (-595522438)
      4     4        (object header)                           c4 7f 00 00 (11000100 01111111 00000000 00000000) (32708)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
################################################################################################################################################################################

##########################################################################情况二############################################################################################
JVM偏向锁已经生效:偏向锁状态下的对象头markword标记:
java.lang.Object object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


------------------------------------------------------------------------------------------------------
t2 内存地址:31874198752,十六进制位:76bd9ace0,二进制位:11101101011110110011010110011100000
线程t2获得锁obj4
偏向锁延迟生效后,线程t2 打印obj5 对象偏向锁状态,其对象头markword标记:
java.lang.Object object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 00 91 86 (00000101 00000000 10010001 10000110) (-2037317627)
      4     4        (object header)                           ed 7f 00 00 (11101101 01111111 00000000 00000000) (32749)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


t2 id:14,二进制位:1110
------------------------------------------------------------------------------------------------------
[线程t3] 获得锁obj5
偏向锁延迟生效后,偏向锁状态-保持:偏向锁 ,其对象头markword标记:
java.lang.Object object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 00 91 86 (00000101 00000000 10010001 10000110) (-2037317627)
      4     4        (object header)                           ed 7f 00 00 (11101101 01111111 00000000 00000000) (32749)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


[线程t4] 获得锁obj5
偏向锁延迟生效后,偏向锁状态-膨胀为:重量级锁 ,其对象头markword标记:
java.lang.Object object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           0a 9c 80 88 (00001010 10011100 10000000 10001000) (-2004837366)
      4     4        (object header)                           ed 7f 00 00 (11101101 01111111 00000000 00000000) (32749)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

################################################################################################################################################################################

##########################################################################情况三############################################################################################
JVM偏向锁已经生效:偏向锁状态下的对象头markword标记:
java.lang.Object object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


------------------------------------------------------------------------------------------------------
t2 内存地址:31874198880,十六进制位:76bd9ad60,二进制位:11101101011110110011010110101100000
线程t2获得锁obj4
偏向锁延迟生效后,线程t2 打印obj5 对象偏向锁状态,其对象头markword标记:
java.lang.Object object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 f8 8e cd (00000101 11111000 10001110 11001101) (-846268411)
      4     4        (object header)                           c9 7f 00 00 (11001001 01111111 00000000 00000000) (32713)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


t2 id:14,二进制位:1110
------------------------------------------------------------------------------------------------------
[线程t3] 获得锁obj5
偏向锁延迟生效后,偏向锁状态-膨胀为:轻量级锁 ,其对象头markword标记:
java.lang.Object object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           08 09 8d 0e (00001000 00001001 10001101 00001110) (244123912)
      4     4        (object header)                           c9 7f 00 00 (11001001 01111111 00000000 00000000) (32713)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


[线程t4] 获得锁obj5
偏向锁延迟生效后,偏向(轻量)锁状态-膨胀为:重量级锁 ,其对象头markword标记:
java.lang.Object object internals:
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           0a 2c 01 cc (00001010 00101100 00000001 11001100) (-872338422)
      4     4        (object header)                           c9 7f 00 00 (11001001 01111111 00000000 00000000) (32713)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

################################################################################################################################################################################
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值