3. JVM查看内存命令及问题定位

1. jstat查看堆内存使用情况

jstat查看堆内存各部分使用量,以及加载类的数量。
命令格式
jstat [-命令选型] [vmid] [时间间隔/毫秒] [查询次数]

1.1 查看class加载情况

[root@localhost bin]# jps
7811 Jps
7725 Bootstrap
[root@localhost bin]# jstat -class 7725
Loaded  Bytes  Unloaded  Bytes     Time   
  3476  7544.4        0     0.0       9.61
  • Loader:加载class数量
  • Bytes:所占用空间大小
  • Unloaded:未加载class数量
  • Bytes:未加载占用空间大小
  • Time:时间

1.2 查看编译情况

[root@localhost bin]# jps
7835 Jps
7725 Bootstrap
[root@localhost bin]# jstat -compiler 7725
Compiled Failed Invalid   Time   FailedType FailedMethod
    2141      0       0     6.99          0 
  • Compiled:编译数量
  • Failed:失败数量
  • Invalid:不可用数量
  • Time:时间
  • FailedType: 失败类型
  • FailedMethod:失败方法

1.3 垃圾回收统一

[root@localhost bin]# jps
7872 Jps
7725 Bootstrap
[root@localhost bin]# jstat -gc 7725
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
832.0  832.0   7.9    0.0    6784.0   1961.1   16880.0    15823.5   24960.0 24218.6 2688.0 2509.4     24    0.479   1      0.025    0.504
#也可以每2秒查询一次,总共查询3次
[root@localhost bin]# jstat -gc 7725 2000 3
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
832.0  832.0   7.9    0.0    6784.0   2031.3   16880.0    15823.5   24960.0 24218.6 2688.0 2509.4     24    0.479   1      0.025    0.504
832.0  832.0   7.9    0.0    6784.0   2031.3   16880.0    15823.5   24960.0 24218.6 2688.0 2509.4     24    0.479   1      0.025    0.504
832.0  832.0   7.9    0.0    6784.0   2031.3   16880.0    15823.5   24960.0 24218.6 2688.0 2509.4     24    0.479   1      0.025    0.504

  • S0C:第一个Survivor区的大小(KB)
  • S1C:第二个Survivor区的大小(KB)
  • S0U:第一个Survivor区的使用大小(KB)
  • S1U:第二个Survivor区的使用大小(KB)
  • EC:Eden区的大小(KB)
  • EU:Eden区的使用大小(KB)
  • OC:Old区大小(KB)
  • OU:Old使用大小(KB)
  • MC:方法区大小(KB)
  • MU:方法区使用大小(KB)
  • CCSC:压缩类空间大小(KB)
  • CCSU:压缩类空间使用大小(KB)
  • YGC:年轻代垃圾回收次数
  • YGCT:年轻代垃圾回收消耗时间
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间

2. jmap的使用及内存溢出分析

2.1 查看内存使用情况

[root@localhost bin]# jps
7910 Jps
7725 Bootstrap
[root@localhost bin]# jmap -heap 7725
Attaching to process ID 7725, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.211-b12

using thread-local object allocation.
Mark Sweep Compact GC

Heap Configuration: #堆内存配置信息
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 255852544 (244.0MB)
   NewSize                  = 5570560 (5.3125MB)
   MaxNewSize               = 85262336 (81.3125MB)
   OldSize                  = 11206656 (10.6875MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage: #堆内存使用情况
New Generation (Eden + 1 Survivor Space): #新生代
   capacity = 7798784 (7.4375MB)
   used     = 4303832 (4.104454040527344MB)
   free     = 3494952 (3.3330459594726562MB)
   55.185936679359244% used
Eden Space:
   capacity = 6946816 (6.625MB)
   used     = 4295720 (4.096717834472656MB)
   free     = 2651096 (2.5282821655273438MB)
   61.83725033166274% used
From Space:
   capacity = 851968 (0.8125MB)
   used     = 8112 (0.0077362060546875MB)
   free     = 843856 (0.8047637939453125MB)
   0.9521484375% used
To Space:
   capacity = 851968 (0.8125MB)
   used     = 0 (0.0MB)
   free     = 851968 (0.8125MB)
   0.0% used
tenured generation: # 老年代
   capacity = 17285120 (16.484375MB)
   used     = 16203232 (15.452606201171875MB)
   free     = 1081888 (1.031768798828125MB)
   93.74092861374407% used

16599 interned Strings occupying 1478152 bytes.

2.2 查看内存对象数量及大小

  • 查看所有对象,包括活跃和非活跃
    jmap -histo | more
  • 查看活跃对象
    jmap -histo:live | more
[root@localhost ~]# jps
8293 Jps
7725 Bootstrap
[root@localhost ~]# jmap -histo:live 7725 | more 

 num     #instances         #bytes  class name
----------------------------------------------
   1:         31171        3084800  [C
   2:          2998        1019856  [I
   3:         30124         722976  java.lang.String
   4:           917         601920  [B
   5:         14199         454368  java.util.HashMap$Node
   6:          3903         448896  java.lang.Class
   7:          4550         400400  java.lang.reflect.Method
   8:          8322         266304  java.util.concurrent.ConcurrentHashMap$Node
   9:          4269         261968  [Ljava.lang.Object;
  10:           839         164856  [Ljava.util.HashMap$Node;
  11:            91         114320  [Ljava.util.concurrent.ConcurrentHashMap$Node;
  12:          6336         101376  java.lang.Object
  13:          1466          70368  java.util.HashMap
  14:          2422          51360  [Ljava.lang.Class;
  15:            87          49264  [Ljava.util.WeakHashMap$Entry;
  16:           931          48032  [Ljava.lang.String;
  17:          1445          46240  java.util.Hashtable$Entry
  18:           929          44592  org.apache.tomcat.util.modeler.AttributeInfo
  19:          1068          42720  java.util.LinkedHashMap$Entry
  20:          1034          33088  java.lang.ref.WeakReference
  21:            41          33040  [S
  22:             8          32896  [Ljava.nio.ByteBuffer;
  23:          1349          32376  java.util.ArrayList
  24:           804          32160  java.lang.ref.SoftReference
  25:           433          31176  org.apache.jasper.compiler.Node$TemplateText
  26:           614          29472  java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync
  27:           390          28080  java.util.logging.Logger
--More--

#对象说明
B byte
C char
D double
F float
I int
J long
Z boolean
[ 数组,如[I表示int[]
[L+类名 其他对象

2.3 将内存情况dump到文件中

#用法:
jmap ‐dump:format=b,file=dumpFileName <pid>
#示例
jmap ‐dump:format=b,file=/tmp/dump.dat 6219
[root@localhost java]# jps
7725 Bootstrap
8333 Jps
[root@localhost java]# jmap -dump:format=b,file=dump.dat 7725
Dumping heap to /usr/local/src/java/dump.dat ...
Heap dump file created
[root@localhost java]# ll
总用量 28096
drwxr-xr-x. 9 root root      220 9月  13 17:16 apache-tomcat-8.5.40
-rw-r--r--. 1 root root  9690027 9月  13 17:11 apache-tomcat-8.5.40.tar.gz
-rw-------. 1 root root 19077697 9月  14 15:50 dump.dat
drwxr-xr-x. 7   10  143      245 4月   2 11:51 jdk1.8.0_211
drwxr-xr-x. 2 root root       56 5月   4 15:31 mysql5.7

2.4 通过jhat对dump文件进行分析

#用法:
jhat ‐port <port> <file>
[root@localhost java]# jhat -port 9999 dump.dat
Reading from dump.dat...
Dump file created Sat Sep 14 15:50:59 CST 2019
Snapshot read, resolving...
Resolving 192797 objects...
WARNING:  Failed to resolve object id 0xf60b0658 for field pkcs8KeySpecClass (signature L)
WARNING:  Failed to resolve object id 0xf60b06c0 for field rsaPrivateCrtKeySpecClass (signature L)
WARNING:  Failed to resolve object id 0xf60b0728 for field rsaPrivateKeySpecClass (signature L)
WARNING:  Failed to resolve object id 0xf60b0790 for field rsaPublicKeySpecClass (signature L)
WARNING:  Failed to resolve object id 0xf5ffb8c8 for field returnType (signature L)
WARNING:  Failed to resolve object id 0xf5f0f758 for field returnType (signature L)

打开浏览器访问http://192.168.66.129:9999/
在这里插入图片描述
最后有OQL功能
在这里插入图片描述
在这里插入图片描述

2.5 通过mat工具对dump进行分析

2.5.1 mat介绍

MAT(Memory Analyzer Tool),一个基于Eclipse的内存分析工具,是一个快速、功能丰
富的JAVA heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。使用内存分析
工具从众多的对象中进行分析,快速的计算出在内存中对象的占用大小,看看是谁阻止
了垃圾收集器的回收工作,并可以通过报表直观的查看到可能造成这种结果的对象。
官网地址:https://www.eclipse.org/mat/

2.5.2 下载安装

地址:https://www.eclipse.org/mat/downloads.php
在这里插入图片描述
解压完启动在这里插入图片描述

2.5.3 使用

在这里插入图片描述
在这里插入图片描述

3. 实战,MAT分析内存

public class TestJvmOutOfMemory {

    // 实现,向集合中添加100万个字符串,每个字符串由100个UUID组成
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            String str = "";
            for (int j = 0; j < 1000; j++) {
                str += UUID.randomUUID().toString();
            }
            list.add(str);
        }
        System.out.println("ok");
    }

}

在这里插入图片描述
参数:

-Xms8m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError

结果如下
在这里插入图片描述
将java_pid8920.hprof导入MAT分析
在这里插入图片描述
分析90.6%都由Object[]占据,比较可疑。
查看详情
在这里插入图片描述

4. jstack的使用

查看正在进行的线程情况,录快照,并且打印出来。

用法:jstack <pid>
[root@localhost java]# jps
8488 Jps
7725 Bootstrap
[root@localhost java]# jstack 7725
2019-09-14 18:06:15
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.211-b12 mixed mode):

"Attach Listener" #44 daemon prio=9 os_prio=0 tid=0x00007fd168003800 nid=0x207a waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"ajp-nio-8009-AsyncTimeout" #42 daemon prio=5 os_prio=0 tid=0x00007fd19050c000 nid=0x1e61 waiting on condition [0x00007fd16c4be000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
	at java.lang.Thread.sleep(Native Method)
	at org.apache.coyote.AbstractProtocol$AsyncTimeout.run(AbstractProtocol.java:1151)
	at java.lang.Thread.run(Thread.java:748)

"ajp-nio-8009-Acceptor-0" #41 daemon prio=5 os_prio=0 tid=0x00007fd19050a000 nid=0x1e60 runnable [0x00007fd16c5bf000]
   java.lang.Thread.State: RUNNABLE
	at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
	at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:422)
	at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:250)
	- locked <0x00000000f6134140> (a java.lang.Object)

4.1 线程的状态

在这里插入图片描述

  • 初始态(NEW)
    创建一个Thread对象,但还未调用start()启动线程时,线程处于初始态。
  • 运行态(RUNNABLE),在Java中,运行态包括 就绪态 和 运行态。
  1. 就绪态
    该状态下的线程已经获得执行所需的所有资源,只要CPU分配执行权就能运
    行。
    所有就绪态的线程存放在就绪队列中。
  2. 运行态
    获得CPU执行权,正在执行的线程。
    由于一个CPU同一时刻只能执行一条线程,因此每个CPU每个时刻只有一条
    运行态的线程。
  • 阻塞态(BLOCKED)
    当一条正在执行的线程请求某一资源失败时,就会进入阻塞态。
    而在Java中,阻塞态专指请求锁失败时进入的状态。
    由一个阻塞队列存放所有阻塞态的线程。
    处于阻塞态的线程会不断请求资源,一旦请求成功,就会进入就绪队列,等待执
    行。
  • 等待态(WAITING)
    当前线程中调用wait、join、park函数时,当前线程就会进入等待态。
    也有一个等待队列存放所有等待态的线程。
    线程处于等待态表示它需要等待其他线程的指示才能继续运行。
    进入等待态的线程会释放CPU执行权,并释放资源(如:锁)
  • 超时等待态(TIMED_WAITING)
    当运行中的线程调用sleep(time)、wait、join、parkNanos、parkUntil时,就
    会进入该状态;
    它和等待态一样,并不是因为请求不到资源,而是主动进入,并且进入后需要其
    他线程唤醒;
    进入该状态后释放CPU执行权 和 占有的资源。
    与等待态的区别:到了超时时间后自动进入阻塞队列,开始竞争锁。
  • 终止态(TERMINATED)
    线程执行结束后的状态。

4.2 实战:死锁问题

4.2.1 构造死锁问题

编写代码,启动2个线程,Thread1拿到了obj1锁,准备去拿obj2锁时,obj2已经被
Thread2锁定,所以发送了死锁。

public class TestDeadLock {

    private static Object obj1 = new Object();

    private static Object obj2 = new Object();

    public static void main(String[] args) {
        new Thread(new Thread1()).start();
        new Thread(new Thread2()).start();
    }

    private static class Thread1 implements Runnable{
        @Override
        public void run() {
            synchronized (obj1){
                System.out.println("Thread1 拿到了 obj1 的锁!");

                try {
                    // 停顿2秒的意义在于,让Thread2线程拿到obj2的锁
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (obj2){
                    System.out.println("Thread1 拿到了 obj2 的锁!");
                }
            }
        }
    }

    private static class Thread2 implements Runnable{
        @Override
        public void run() {
            synchronized (obj2){
                System.out.println("Thread2 拿到了 obj2 的锁!");

                try {
                    // 停顿2秒的意义在于,让Thread1线程拿到obj1的锁
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (obj1){
                    System.out.println("Thread2 拿到了 obj1 的锁!");
                }
            }
        }
    }
}

4.2.2 在linux执行

[root@localhost TestDeadLock]# ll
总用量 4
-rw-r--r--. 1 root root 1517 9月  14 18:35 TestDeadLock.java
[root@localhost TestDeadLock]# javac TestDeadLock.java 
[root@localhost TestDeadLock]# ll
总用量 20
-rw-r--r--. 1 root root  184 9月  14 21:35 TestDeadLock$1.class
-rw-r--r--. 1 root root  843 9月  14 21:35 TestDeadLock.class
-rw-r--r--. 1 root root 1517 9月  14 18:35 TestDeadLock.java
-rw-r--r--. 1 root root 1078 9月  14 21:35 TestDeadLock$Thread1.class
-rw-r--r--. 1 root root 1078 9月  14 21:35 TestDeadLock$Thread2.class
[root@localhost TestDeadLock]# java TestDeadLock
Thread1 拿到了 obj1 的锁!
Thread2 拿到了 obj2 的锁!

此时重新打开一个连接
在这里插入图片描述

4.2.3 使用jstack分析问题

[root@localhost ~]# jps
8807 Jps
8794 TestDeadLock
7725 Bootstrap
[root@localhost ~]# jstack 8794
2019-09-14 21:37:34
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.211-b12 mixed mode):

"Attach Listener" #11 daemon prio=9 os_prio=0 tid=0x00007fbd8c001000 nid=0x227b waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"DestroyJavaVM" #10 prio=5 os_prio=0 tid=0x00007fbdb4009800 nid=0x225b waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Thread-1" #9 prio=5 os_prio=0 tid=0x00007fbdb40cc000 nid=0x2265 waiting for monitor entry [0x00007fbdb81e8000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at TestDeadLock$Thread2.run(TestDeadLock.java:46)
	- waiting to lock <0x00000000f0c5cc68> (a java.lang.Object)
	- locked <0x00000000f0c5cc78> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:748)

"Thread-0" #8 prio=5 os_prio=0 tid=0x00007fbdb40ca000 nid=0x2264 waiting for monitor entry [0x00007fbdb82e9000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at TestDeadLock$Thread1.run(TestDeadLock.java:26)
	- waiting to lock <0x00000000f0c5cc78> (a java.lang.Object)
	- locked <0x00000000f0c5cc68> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:748)

"Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007fbdb40b4000 nid=0x2262 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007fbdb40b1000 nid=0x2261 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007fbdb40af000 nid=0x2260 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007fbdb40ad800 nid=0x225f runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007fbdb407a800 nid=0x225e in Object.wait() [0x00007fbdb88ef000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x00000000f0c08ed0> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
	- locked <0x00000000f0c08ed0> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
	at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)

"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007fbdb4077800 nid=0x225d in Object.wait() [0x00007fbdb89f0000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x00000000f0c06bf8> (a java.lang.ref.Reference$Lock)
	at java.lang.Object.wait(Object.java:502)
	at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
	- locked <0x00000000f0c06bf8> (a java.lang.ref.Reference$Lock)
	at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=0 tid=0x00007fbdb406e000 nid=0x225c runnable 

"VM Periodic Task Thread" os_prio=0 tid=0x00007fbdb40b7000 nid=0x2263 waiting on condition 

JNI global references: 5


Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x00007fbd980062c8 (object 0x00000000f0c5cc68, a java.lang.Object),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x00007fbd98004e28 (object 0x00000000f0c5cc78, a java.lang.Object),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
	at TestDeadLock$Thread2.run(TestDeadLock.java:46)
	- waiting to lock <0x00000000f0c5cc68> (a java.lang.Object) # 等待获取锁
	- locked <0x00000000f0c5cc78> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:748)
"Thread-0":
	at TestDeadLock$Thread1.run(TestDeadLock.java:26)
	- waiting to lock <0x00000000f0c5cc78> (a java.lang.Object)  # 等待获取锁
	- locked <0x00000000f0c5cc68> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

关键看Java stack information for the threads listed above: 发生死锁。

5. VisualVM工具的使用

VisualVM,能够监控线程,内存情况,查看方法的CPU时间和内存中的对 象,已被GC的
对象,反向查看分配的堆栈(如100个String对象分别由哪几个对象分配出来的)。
VisualVM使用简单,几乎0配置,功能还是比较丰富的,几乎囊括了其它JDK自带命令的
所有功能。

  • 内存信息
  • 线程信息
  • Dump堆(本地进程)
  • Dump线程(本地进程)
  • 打开堆Dump。堆Dump可以用jmap来生成。
  • 打开线程Dump
  • 生成应用快照(包含内存信息、线程信息等等)
  • 性能分析。CPU分析(各个方法调用时间,检查哪些方法耗时多),内存分析(各类
    对象占用的内存,检查哪些类占用内存多)

5.1 启动

在jdk安装目录下的bin目录下,找到jvisualvm.exe
在这里插入图片描述

5.2 查看本地进程

在这里插入图片描述

5.3 查看cpu、内存、类和线程信息

在这里插入图片描述

5.4 查看线程详情

在这里插入图片描述
也可以点击右上角的dump,其实就是执行jstack命令。

5.5 抽样器

在这里插入图片描述

5.6 远程监控

需要借助JMX技术实现。

5.6.1 什么是JMX

JMX(Java Management Extensions,即Java管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。

5.6.2 远程监控tomcat的配置

在tomcat的bin目录下,修改catalina.sh, 添加如下参数:

注意关闭防火墙
JAVA_OPTS="$JAVA_OPTS  -Dcom.sun.management.jmxremote=true
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Djava.rmi.server.hostname=192.168.66.129"
#这几个参数的意思是:
#‐Dcom.sun.management.jmxremote :允许使用JMX远程管理
#‐Dcom.sun.management.jmxremote.port=9999 :JMX远程连接端口
#‐Dcom.sun.management.jmxremote.authenticate=false :不进行身份认证,任何用
户都可以连接
#‐Dcom.sun.management.jmxremote.ssl=false :不使用ssl

保存退出,重启tomcat

5.6.3 远程监控

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值