JVM4-工具使用

30 篇文章 0 订阅

1 虚拟机工具介绍

系统定位问题:知识和经验是基础,数据是依据,工具是运用知识处理书的手段.
D:\Program Files\Java\jdk1.8.0_92\bin\jps,jstat,jstack等
工具都很小,是因为引用了tools.jar包(D:\Program Files\Java\jdk1.8.0_92\lib\tools.jar)中的内容.
在tools.jar中bin下的同样的目录文件 tools.jar\sun\tools\jps,jstat,jstack等

2 虚拟机工具-jps详解

C:\Users\bob>jps -help;
illegal argument: -help;
usage: jps [-help]
       jps [-q] [-mlvV] [<hostid>]

Definitions:
    <hostid>:      <hostname>[:<port>]

2.1 jps

java process status
显示的为本地虚拟机唯一ID,lvmid. local virtual machine id .

C:\Users\bob>jps
12256 Launcher
12336 Jps   //本身是一个进程
11000       //打开的IDEA,也就是PID,
13948 RemoteMavenServer

如果运行了main程序,也能通过jps查看到进程. 如:

12345 Main
12346 Main

2.2 jps -l

显示完整类方法或jar文件

2.3 jps -m

运行主类所接受的程序参数(是eclipse或IEDA中配置的program arguments)

2.4 jps -v

运行主类接受的JVM参数(eclipse中VM arguments和IDEA中的VM options)

C:\Users\bobshute>jps -v
8036 Jps -Dapplication.home=D:\Program Files\Java\jdk1.8.0_92 -Xms8m


C:\Users\bobshute>jps -v
10900  -Xms128m -Xmx750m -XX:ReservedCodeCacheSize=240m -XX:+UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=50 -ea -Dsun.io.useCanonCaches=false -Djava.net.pref
erIPv4Stack=true -XX:+HeapDumpOnOutOfMemoryError -XX:-OmitStackTraceInFastThrow
-Djb.vmOptionsFile=D:\ideaIU-2017.3.1.win(1)\bin\idea64.exe.vmoptions -Xbootclas
spath/a:D:\ideaIU-2017.3.1.win(1)\lib\boot.jar -Didea.jre.check=true -Didea.path
s.selector=IntelliJIdea2017.3 -XX:ErrorFile=C:\Users\bobshute\java_error_in_idea_%p
.log -XX:HeapDumpPath=C:\Users\bobshute\java_error_in_idea.hprof

10808 RemoteMavenServer -Djava.awt.headless=true -Didea.version==2017.3.1 -Xmx76
8m -Didea.maven.embedder.version=3.3.9 -Dfile.encoding=GBK

10620  -Dosgi.requiredJavaVersion=1.8 -Xms40m -Dosgi.module.lock.timeout=10 -Xve
rify:none -Dorg.eclipse.swt.browser.IEVersion=10001 -Xmx1200m

4556 Jps -Dapplication.home=D:\Program Files\Java\jdk1.8.0_92 -Xms8m

其中10900 为IDEA ;10620为eclipse.

2.5 jps -lmv

可以一起用多个参数.

3 虚拟机工具-jstat详解

3.1 简介

类装载,内存,垃圾收集,jit编译的信息

Jstat是JDK自带的一个轻量级小工具。全称“Java Virtual Machine statistics monitoring tool”,它位于java的bin目录下,主要利用JVM内建的指令对Java应用程序的资源和性能进行实时的命令行的监控,包括了对Heap size和垃圾回收状况的监控。可见,Jstat是轻量级的、专门针对JVM的工具,非常适用。

jstat工具特别强大,有众多的可选项,详细查看堆内各个部分的使用量,以及加载类的数量。使用时,需加上查看进程的进程id,和所选参数。参考格式如下:

C:\Users\bobshute>jstat -help
invalid argument count
Usage: jstat -help|-options
       jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

Definitions:
  <option>      An option reported by the -options option
  <vmid>        Virtual Machine Identifier. A vmid takes the following form:
                     <lvmid>[@<hostname>[:<port>]]
                Where <lvmid> is the local vm identifier for the target
                Java virtual machine, typically a process id; <hostname> is
                the name of the host running the target Java virtual machine;
                and <port> is the port number for the rmiregistry on the
                target host. See the jvmstat documentation for a more complete
                description of the Virtual Machine Identifier.
  <lines>       Number of samples between header lines.
  <interval>    Sampling interval. The following forms are allowed:
                    <n>["ms"|"s"]
                Where <n> is an integer and the suffix specifies the units as
                milliseconds("ms") or seconds("s"). The default units are "ms".
  <count>       Number of samples to take before terminating.
  -J<flag>      Pass <flag> directly to the runtime system.

C:\Users\bobshute>
jstat -options 

可以列出当前JVM版本支持的选项,常见的有

class (类加载器) 
  compiler (JIT) 
  gc (GC堆状态) 
  gccapacity (各区大小) 
  gccause (最近一次GC统计和原因) 
  gcnew (新区统计)
  gcnewcapacity (新区大小)
  gcold (老区统计)
  gcoldcapacity (老区大小)
  gcpermcapacity (永久区大小)
  gcutil (GC统计汇总)
  printcompilation (HotSpot编译统计)

jstat -gccause:额外输出上次GC原因

3.2 GC统计汇总

jstat [-命令选项] [vmid(进程号)] [间隔时间/毫秒] [查询次数]

jstat -gcutil 6692 1000 10
每隔1000ms打印一次,打印10次

C:\Users\bobshute>jstat -gcutil 10004
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT

  0.00  99.82  25.88  74.20  90.28  80.46     41    1.498     8    2.299    3.77

C:\Users\bobshute>
S0:Survivor0
S2:Survivor1
E:Eden
O:old space
M:metaspace 元空间
CCS:Compressed class space
YGC: Number of young generation garbage collection events.

详细如下: 
-gc option
Garbage-collected heap statistics.

S0C: Current survivor space 0 capacity (kB).

S1C: Current survivor space 1 capacity (kB).

S0U: Survivor space 0 utilization (kB).

S1U: Survivor space 1 utilization (kB).

EC: Current eden space capacity (kB).

EU: Eden space utilization (kB).

OC: Current old space capacity (kB).

OU: Old space utilization (kB).

MC: Metaspace capacity (kB).

MU: Metacspace utilization (kB).

CCSC: Compressed class space capacity (kB).

CCSU: Compressed class space used (kB).

YGC: Number of young generation garbage collection events.

YGCT: Young generation garbage collection time.

FGC: Number of full GC events.

FGCT: Full garbage collection time.

GCT: Total garbage collection time.

3.3 类加载统计

C:\Users\bobshute>jstat -class 18036
Loaded  Bytes  Unloaded  Bytes     Time
 43242 46208.3      273   207.0      66.19

Loaded:加载class的数量
Bytes:所占用空间大小
Unloaded:未加载数量
Bytes:未加载占用空间
Time:时间

3.4 编译统计

C:\Users\bobshute>jstat -compiler 18036
Compiled Failed Invalid   Time   FailedType FailedMethod
   25356      1       0   108.24          1 com/intellij/openapi/vfs/newvfs/impl
/VirtualDirectoryImpl a

Compiled:编译数量。
Failed:失败数量
Invalid:不可用数量
Time:时间
FailedType:失败类型
FailedMethod:失败的方法

3.5 垃圾回收统计

C:\Users\bobshute>jstat -gc 18036
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU
   CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
4352.0 4352.0  0.0   1415.1 34944.0  26192.1   197676.0   173809.6  189312.0 182
286.3  0.0    0.0       93    1.064  10      0.348    1.412

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

3.6 堆内存统计

C:\Users\bobshute>jstat -gccapacity 18036
 NGCMN    NGCMX     NGC     S0C   S1C       EC      OGCMN      OGCMX       OGC
       OC       MCMN     MCMX      MC     CCSMN    CCSMX     CCSC    YGC    FGC

 43648.0 174720.0  43648.0 4352.0 4352.0  34944.0    87424.0   349568.0   197676
.0   197676.0      0.0 189824.0 189440.0      0.0      0.0      0.0     94    10

NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
S0C:第一个幸存区大小
S1C:第二个幸存区的大小
EC:伊甸园区的大小
OGCMN:老年代最小容量
OGCMX:老年代最大容量
OGC:当前老年代大小
OC:当前老年代大小
MCMN:最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:年轻代gc次数
FGC:老年代GC次数

3.7 新生代垃圾回收统计

C:\Users\bobshute>jstat -gcnew 18036
 S0C    S1C    S0U    S1U   TT MTT  DSS      EC       EU     YGC     YGCT
4352.0 4352.0    0.0  474.0  6   6 2176.0  34944.0  12352.2     97    1.086

S0C:第一个幸存区大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
TT:对象在新生代存活的次数
MTT:对象在新生代存活的最大次数
DSS:期望的幸存区大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间

3.8 新生代内存统计

C:\Users\bobshute>jstat -gcnewcapacity 18036
  NGCMN      NGCMX       NGC      S0CMX     S0C     S1CMX     S1C       ECMX
    EC      YGC   FGC
   43648.0   174720.0    43648.0  17472.0   4352.0  17472.0   4352.0   139776.0
   34944.0   100    10

NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
S0CMX:最大幸存1区大小
S0C:当前幸存1区大小
S1CMX:最大幸存2区大小
S1C:当前幸存2区大小
ECMX:最大伊甸园区大小
EC:当前伊甸园区大小
YGC:年轻代垃圾回收次数
FGC:老年代回收次数

3.9 老年代垃圾回收统计

C:\Users\bobshute>jstat -gcold 18036
   MC       MU      CCSC     CCSU       OC          OU       YGC    FGC    FGCT
    GCT
189440.0 182421.0      0.0      0.0    197676.0    174628.4    101    10    0.34
8    1.456

MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
OC:老年代大小
OU:老年代使用大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

3.10 老年代内存统计


C:\Users\bobshute>jstat -gcoldcapacity  18036
   OGCMN       OGCMX        OGC         OC       YGC   FGC    FGCT     GCT
    87424.0    349568.0    197676.0    197676.0   103    10    0.348    1.468

OGCMN:老年代最小容量
OGCMX:老年代最大容量
OGC:当前老年代大小
OC:老年代大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

3.11 元数据空间统计

C:\Users\bobshute>jstat -gcmetacapacity   18036
   MCMN       MCMX        MC       CCSMN      CCSMX       CCSC     YGC   FGC
FGCT     GCT
       0.0   189824.0   189440.0        0.0        0.0        0.0   103    10
 0.348    1.468

MCMN:最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时

3.12 总结垃圾回收统计


C:\Users\bobshute>jstat -gcutil   18036
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT

  3.76   0.00  12.85  88.34  96.30      -    104    1.127    10    0.348    1.47
5

S0:幸存1区当前使用比例
S1:幸存2区当前使用比例
E:伊甸园区使用比例
O:老年代使用比例
M:元数据区使用比例
CCS:压缩使用比例
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

3.13 JVM编译方法统计


C:\Users\bobshute>jstat -printcompilation  18036
Compiled  Size  Type Method
   25815    109    1 com/intellij/openapi/progress/impl/ProgressManagerImpl$1$$L
ambda$89 run

Compiled:最近编译方法的数量
Size:最近编译方法的字节码数量
Type:最近编译方法的编译类型
Method:方法名标识。

3.14 元空间

JDK7包括7之后永久代消失,取而代之的为元空间(M).
元空间的本质和永久代类似,都是对JVM规范中方法区的实现。
不过元空间与永久代之间最大的区别在于:
元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制.

3.15 参考文章

含义官方详解
jstat命令详解
jstat命令使用

4 虚拟机工具-jinfo

4.1 简介

实时查看和调整虚拟机的各项参数
jinfo中的一些命令只能在linux中执行,不能在windows中执行.

C:\Users\bobshute>jinfo
Usage:
    jinfo [option] <pid>
        (to connect to running process)
    jinfo [option] <executable <core>
        (to connect to a core file)
    jinfo [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server)

where <option> is one of:
    -flag <name>         to print the value of the named VM flag
    -flag [+|-]<name>    to enable or disable the named VM flag
    -flag <name>=<value> to set the named VM flag to the given value
    -flags               to print VM flags
    -sysprops            to print Java system properties
    <no option>          to print both of the above
    -h | -help           to print this help message

4.2 jinfo -flag

jinfo -flag UseSerialGC 6574// 6574进程 是否使用serialGC垃圾回收器

jinfo -flag UseG1GC 6574 //6574进程 是否使用G1GC垃圾回收器.

C:\Users\bobshute>jinfo -flag UseSerialGC 6312
-XX:-UseSerialGC
//-XX代表虚拟机参数,-号代表没有启用
C:\Users\bobshute>jinfo -flag UseG1GC 6312
-XX:+UseG1GC
//-XX代表虚拟机参数,+号代表启用
C:\Users\bobshute>

4.3 jinfo -flag +/-

调整参数,无需重启

jinfo
 -flag [+|-]<name>    to enable or disable the named VM flag
-flag <name>=<value> to set the named VM flag to the given value
//加入jvm参数,无需重启,6312为进程号
C:\Users\bobshute>jinfo -flag +PrintGC 6312
-XX:-PrintGC

C:\Users\bobshute>jinfo -flag +PrintGCDetails 6312
-XX:-PrintGC

C:\Users\bobshute>jinfo -flag +PrintGCTimeStamps 6312
-XX:-PrintGC

//去掉jvm参数,无需重启
C:\Users\bobshute>jinfo -flag -PrintGC 6312
-XX:-PrintGC

4.4 jinfo -flags

显示当前使用的JVM参数

C:\Users\bobshute>jinfo -flags 6312
Attaching to process ID 6312, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.92-b14
Non-default VM flags: -XX:-BytecodeVerificationLocal -XX:-BytecodeVerificationRe
mote -XX:CICompilerCount=3 -XX:InitialHeapSize=41943040 -XX:MaxHeapSize=12582912
00 -XX:MaxNewSize=419430400 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=13631488 -X
X:OldSize=28311552 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+U
seFastUnorderedTimeStamps -XX:-UseLargePagesIndividualAllocation -XX:+UseParalle
lGC
Command line:  -Dosgi.requiredJavaVersion=1.8 -Xms40m -Dosgi.module.lock.timeout
=10 -Xverify:none -Dorg.eclipse.swt.browser.IEVersion=10001 -Xmx1200m

C:\Users\bobshute>

5. 虚拟机工具-jmap详解

5.1 简介

5.1.1 介绍

查看整个JVM内存状态
jmap命令(Java Memory Map):主要用于打印指定Java进程(或核心文件、远程调试服务器)的共享对象内存映射或堆内存细节。
jmap命令可以获得运行中的jvm的堆的快照,从而可以离线分析堆,以检查内存泄漏,检查一些严重影响性能的大对象的创建,检查系统中什么对象最多,各种对象所占内存的大小等等。可以使用jmap生成Heap Dump。

java memory = direct memory(直接内存) + jvm memory(MaxPermSize +Xmx)

jmqp中的一些命令只能在linux中执行,不能在windows中执行.
通过jhat来分析DUMP信息: jhat是sun 1.6及以上版本中自带的一个用于分析JVM 堆DUMP 文件的工具,基于此工具可分析JVM HEAP 中对象的内存占用情况

从安全点日志看,从Heap Dump开始,整个JVM都是停顿的,考虑到IO(虽是写到Page Cache,但或许会遇到background flush),几G的Heap可能产生几秒的停顿,在生产环境上执行时谨慎再谨慎。

C:\Users\bobshute>jmap
Usage:
    jmap [option] <pid>
        (to connect to running process)
    jmap [option] <executable <core>
        (to connect to a core file)
    jmap [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server)

where <option> is one of:
    <none>               to print same info as Solaris pmap
    -heap                to print java heap summary
    -histo[:live]        to print histogram of java object heap; if the "live"
                         suboption is specified, only count live objects
    -clstats             to print class loader statistics
    -finalizerinfo       to print information on objects awaiting finalization
    -dump:<dump-options> to dump java heap in hprof binary format
                         dump-options:
                           live         dump only live objects; if not specified
,
                                        all objects in the heap are dumped.
                           format=b     binary format
                           file=<file>  dump heap to <file>
                         Example: jmap -dump:live,format=b,file=heap.bin <pid>
    -F                   force. Use with -dump:<dump-options> <pid> or -histo
                         to force a heap dump or histogram when <pid> does not
                         respond. The "live" suboption is not supported
                         in this mode.
    -h | -help           to print this help message
    -J<flag>             to pass <flag> directly to the runtime system

C:\Users\bobshute>

5.1.2 基本参数

  • 如果使用不带选项参数的jmap打印共享对象映射,将会打印目标虚拟机中加载的每个共享对象的起始地址、映射大小以及共享对象文件的路径全称。这与Solaris的pmap工具比较相似。
  • -dump:[live,]format=b,file= 使用hprof二进制形式,输出jvm的heap内容到文件, live子选项是可选的,假如指定live选项,那么只输出活的对象到文件.
  • -finalizerinfo 打印正等候回收的对象的信息.
  • -heap 打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情况.
  • -histo[:live] 打印每个class的实例数目,内存占用,类全名信息. VM的内部类名字开头会加上前缀”*”. 如果live子参数加上后,只统计活的对象数量.
  • -permstat 打印classload和jvm heap长久层的信息. 包含每个classloader的名字,活泼性,地址,父classloader和加载的class数量. 另外,内部String的数量和占用内存数也会打印出来.
  • -F 强迫.在pid没有响应的时候使用-dump或者-histo参数. 在这个模式下,live子参数无效.
  • -h | -help 打印辅助信息
  • -J 传递参数给jmap启动的jvm.
  • pid 需要被打印配相信息的java进程id,可以用jps查问

5.1.3 dump文件分析

Dump出来的文件建议用JDK自带的VisualVM或Eclipse的MAT插件打开,对象的大小有两种统计方式:

本身大小(Shallow Size):对象本来的大小。
保留大小(Retained Size): 当前对象大小 + 当前对象直接或间接引用到的对象的大小总和。
看本身大小时,占大头的都是char[] ,byte[]之类的,没什么意思(用jmap -histo:live pid 看的也是本身大小)。所以需要关心的是保留大小比较大的对象,看谁在引用这些char[], byte[]。

(MAT能看的信息更多,但VisualVM胜在JVM自带,用法如下:命令行输入jvisualvm,文件->装入->堆Dump->检查 -> 查找20保留大小最大的对象,就会触发保留大小的计算,然后就可以类视图里浏览,按保留大小排序了)

5.2 jmap - dump

5.2.1 用法

导出堆快照信息,后续可以进行分析
- -live 导出存活的
- -format 格式
- -file 文件地址

C:\Users\bobshute>jmap -dump:format=b,file=d:\a.bin 10492
Dumping heap to D:\a.bin ...
Heap dump file created

C:\Users\bobshute>

5.2.2 分析内存泄漏OOM通常用法

  • 1)也可以通过该JVM配置显示Dump信息: -XX:+HeapDumpOnOutOfMemoryError   
    让JVM在遇到OutOfMemoryError时自动生成Dump文件
  • 2)jmap -dump:format=b,file=/path/heap.bin 进程ID
    如果只dump heap中的存活对象,则加上选项-live。
  • 3)然后使用MAT分析工具,如jhat命令,eclipse的mat插件。
    最后在eclipse中安装MAT插件(http://www.eclipse.org/mat/),然后在eclipse中,file—->open,打开这个文件heap.bin,利用现成的OOM工具进行分析。
    具体操作方法:
    首先输入网址http://www.eclipse.org/mat/previousReleases.php,然后查看你自己的Eclipse版本,我的是Indigo的,所以点击链接“Previous Releases”,选择Indigo版本的URLhttp://download.eclipse.org/mat/1.1.0/update-site/

5.3 jmap -heap [pid]

打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情况
要注意的是在使用CMS GC 情况下,jmap -heap的执行有可能会导致JAVA 进程挂起

ps -ef|grep tomcat  #获取tomcat的pid

[root@localhost ~]# jmap -heap 27900
Attaching to process ID 27900, please wait...
Debugger attached successfully.
Client compiler detected.
JVM version is 20.45-b01
using thread-local object allocation.
Mark Sweep Compact GC
Heap Configuration: #堆内存初始化配置
   MinHeapFreeRatio = 40     #-XX:MinHeapFreeRatio设置JVM堆最小空闲比率  
   MaxHeapFreeRatio = 70   #-XX:MaxHeapFreeRatio设置JVM堆最大空闲比率  
   MaxHeapSize = 100663296 (96.0MB)   #-XX:MaxHeapSize=设置JVM堆的最大大小
   NewSize = 1048576 (1.0MB)     #-XX:NewSize=设置JVM堆的‘新生代’的默认大小
   MaxNewSize = 4294901760 (4095.9375MB) #-XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小
   OldSize = 4194304 (4.0MB)  #-XX:OldSize=设置JVM堆的‘老生代’的大小
   NewRatio = 2    #-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
   SurvivorRatio = 8  #-XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值
   PermSize = 12582912 (12.0MB) #-XX:PermSize=<value>:设置JVM堆的‘持久代’的初始大小  
   MaxPermSize = 67108864 (64.0MB) #-XX:MaxPermSize=<value>:设置JVM堆的‘持久代’的最大大小  
Heap Usage:
New Generation (Eden + 1 Survivor Space): #新生代区内存分布,包含伊甸园区+1个Survivor区
   capacity = 30212096 (28.8125MB)
   used = 27103784 (25.848182678222656MB)
   free = 3108312 (2.9643173217773438MB)
   89.71169693092462% used
Eden Space: #Eden区内存分布
   capacity = 26869760 (25.625MB)
   used = 26869760 (25.625MB)
   free = 0 (0.0MB)
   100.0% used
From Space: #其中一个Survivor区的内存分布
   capacity = 3342336 (3.1875MB)
   used = 234024 (0.22318267822265625MB)
   free = 3108312 (2.9643173217773438MB)
   7.001809512867647% used
To Space: #另一个Survivor区的内存分布
   capacity = 3342336 (3.1875MB)
   used = 0 (0.0MB)
   free = 3342336 (3.1875MB)
   0.0% used
tenured generation:   #当前的Old区内存分布  
   capacity = 67108864 (64.0MB)
   used = 67108816 (63.99995422363281MB)
   free = 48 (4.57763671875E-5MB)
   99.99992847442627% used
Perm Generation:     #当前的 “持久代” 内存分布
   capacity = 14417920 (13.75MB)
   used = 14339216 (13.674942016601562MB)
   free = 78704 (0.0750579833984375MB)
   99.45412375710227% used

5.4 jmap -histo 10492

显示类和实例的数量
打印每个class的实例数目,内存占用,类全名信息. VM的内部类名字开头会加上前缀”*”. 如果live子参数加上后,只统计活的对象数量.

C:\Users\bobshute>jmap -histo 10492
显示很多
*
*
10778:             1             16  sun.util.resources.LocaleData
10779:             1             16  sun.util.resources.LocaleData$LocaleDataRes
ourceBundleControl
Total       6683308      329071952

//序号、instances(实例数)、bytes(大小)、classs name(类名)。它基本是按照使用使用大小逆序排列的。 

#instance 是对象的实例个数 
#bytes 是总占用的字节数 
class name 对应的就是 Class 文件里的 class 的标识 
B 代表 byte
C 代表 char
D 代表 double
F 代表 float
I 代表 int
J 代表 long
Z 代表 boolean
前边有 [ 代表数组, [I 就相当于 int[]
对象用 [L+ 类名表示

显示很多可以通过grep来查看
jmap -histo 10492 |grep //过滤看
jmap -histo 10492 |more //分页看

5.5 jmap -histo:live pid

jmap -histo:live pid>a.log
可以观察heap中所有对象的情况(heap中所有生存的对象的情况)。包括对象数量和所占空间大小。 可以将其保存到文本中去,在一段时间后,使用文本对比工具,可以对比出GC回收了哪些对象。

jmap -histo:live 这个命令执行,JVM会先触发gc,然后再统计信息。

5.6 总结

  • 1.如果程序内存不足或者频繁GC,很有可能存在内存泄露情况,这时候就要借助Java堆Dump查看对象的情况。
  • 2.要制作堆Dump可以直接使用jvm自带的jmap命令
  • 3.可以先使用jmap -heap命令查看堆的使用情况,看一下各个堆空间的占用情况。
  • 4.使用jmap -histo:[live]查看堆内存中的对象的情况。如果有大量对象在持续被引用,并没有被释放掉,那就产生了内存泄露,就要结合代码,把不用的对象释放掉。
  • 5.也可以使用 jmap -dump:format=b,file=命令将堆信息保存到一个文件中,再借助jhat命令查看详细内容
  • 6.在内存出现泄露、溢出或者其它前提条件下,建议多dump几次内存,把内存文件进行编号归档,便于后续内存整理分析。
  • 7.在用cms gc的情况下,执行jmap-heap有些时候会导致进程变T,因此强烈建议别执行这个命令,如果想获取内存目前每个区域的使用状况,可通过jstat -gc或jstat -gccapacity来拿到。

5.7 参考文章(值得学习)

java命令–jmap命令使用(附分析过程)

6.eclipse Memory Analyzer分析工具

Eclipse 提供的一个用于分析JVM 堆Dump文件的插件。借助这个插件可查看对象的内存占用状况,引用关系,分析内存泄露等。
http://www.eclipse.org/mat/

7 虚拟机工具-jhat详解

7.1 基础

jhat:jvm heap Analysis Tool 分析jmap导出的jvm内存信息.
很少使用,因为:
1.分析时占用cpu和内存.所以一般不在服务器上分析.
2.下载到本地之后的分析, 一般通过工具来分析.

C:\Users\bobshute>jhat d:\a.bin
Reading from d:\a.bin...
Dump file created Sun Dec 24 15:44:46 CST 2017
Snapshot read, resolving...
Resolving 10465204 objects...
Chasing references, expect 2093 dots............................................
................................................................................
.................................................
Eliminating duplicate references................................................
................................................................................ 
.............................................
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

然后可以通过http://localhost:7000/的地址来访问.
查看到的页面显示如下:

All Classes (excluding platform)

Package <Arrays>

class [Laj.org.objectweb.asm.Attribute; [0xfa909a48]
class [Laj.org.objectweb.asm.Item; [0xfcc695d0]
class [Laj.org.objectweb.asm.Label; [0xfcc692f8]
class [Laj.org.objectweb.asm.Type; [0xfcc69290]
class [Lch.qos.logback.core.joran.spi.ConsoleTarget; [0xb7053a98]
class [Lch.qos.logback.core.pattern.parser.TokenStream$TokenizerState; [0xb7053a30]

........中间很多.......


class scalariform.formatter.preferences.SpacesAroundMultiImports$ [0xb8ace758]
class scalariform.formatter.preferences.SpacesWithinPatternBinders$ [0xb8ace478]

Other Queries(以下为链接可以点击访问)

All classes including platform
Show all members of the rootset
Show instance counts for all classes (including platform)
Show instance counts for all classes (excluding platform)
Show heap histogram
Show finalizer summary
Execute Object Query Language (OQL) query

最后一个的链接地址为: http://localhost:7000/oql/,可以在该页面中查询sql一样查询对象. 
例如:select from java.lang.String s where s.value.length>1000

最后一个的链接地址为: http://localhost:7000/oql/,可以在该页面中查询sql一样查询对象.
例如:select from java.lang.String s where s.value.length>1000

8 虚拟机工具-jstack详解

8.1 简介

jstack是java虚拟机自带一种堆栈跟踪工具。jstack命令主要用来查看Java线程的调用堆栈的,可以用来分析线程问题(如死锁.死循环)。
jstack用于打印出java进程ID或corefile或远程调试服务的Java堆栈信息,
如果是在64位机器上,需要指定选项”-J-d64”,Windows的jstack使用方式只支持以下的这种方式:

jstack [-l] pid

主要分为两个功能:
1).针对活着的进程做本地的或远程的线程dump;
2).针对core文件做线程dump。

Jstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。

8.2 命令格式

C:\Users\bobshute>jstack
Usage:
    jstack [-l] <pid>
        (to connect to running process)
    jstack -F [-m] [-l] <pid>
        (to connect to a hung process)
    jstack [-m] [-l] <executable> <core>
        (to connect to a core file)
    jstack [-m] [-l] [server_id@]<remote server IP or hostname>
        (to connect to a remote debug server)

Options:
    -F  to force a thread dump. Use when jstack <pid> does not respond (process 
is hung) //强制打印
    -m  to print both java and native frames (mixed mode)//java和本地方法栈
    -l  long listing. Prints additional information about locks//额外的信息,比如锁信息
    -h or -help to print this help message
jstack [ option ] pid
jstack [ option ] executable core   //core 将被打印信息的core dump文件
jstack [ option ] [server-id@]remote-hostname-or-IP //远程debug服务的主机名或ip,server-id 唯一id,假如一台主机上多个远程debug服务 

jstack -l 4089 >1.txt //导出到文件.
-F 当’jstack [-l] pid’没有相应的时候强制打印栈信息,如果直接jstack无响应时,用于强制jstack),一般情况不需要使用

-l 长列表. 打印关于锁的附加信息,例如属于java.util.concurrent的ownable synchronizers列表,会使得JVM停顿得长久得多(可能会差很多倍,比如普通的jstack可能几毫秒和一次GC没区别,加了-l 就是近一秒的时间),-l 建议不要用。一般情况不需要使用

-m 打印java和native c/c++框架的所有栈信息.可以打印JVM的堆栈,显示上Native的栈帧,一般应用排查不需要使用

-h | -help打印帮助信息

pid 需要被打印配置信息的java进程id,可以用jps查询.

一般定位线程长时间停顿的原因. ,代码编写中最好写有线程的名字,便于查看线程信息.

8.3 通过代码查看所有信息

import java.util.Map;

public class AllStackTraces {
    public static void main(String[] args) {

        for (Map.Entry<Thread, StackTraceElement[]> stackTrace : Thread.getAllStackTraces().entrySet())
        {
            Thread thread = (Thread) stackTrace.getKey();
            StackTraceElement[] stack = (StackTraceElement[]) stackTrace.getValue();
            if (thread.equals(Thread.currentThread())) {
                continue;
            }
            System.out.println("线程:" + thread.getName());
            for (StackTraceElement stackTraceElement : stack) {
                System.out.println(stackTraceElement);
            }
        }
    }
}

打印结果:
"D:\Program Files\Java\jdk1.8.0_92\bin\java" -javaagent:D:\ideaIU-2017.3.1.win(1)\lib\idea_rt.jar=61728:D:\ideaIU-2017.3.1.win(1)\bin -Dfile.encoding=UTF-8 -classpath "D:\Program Files\Java\jdk1.8.0_92\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\deploy.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\ext\localedata.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\ext\sunec.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\ext\zipfs.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\javaws.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\jce.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\jfxswt.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\management-agent.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\plugin.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\resources.jar;D:\Program Files\Java\jdk1.8.0_92\jre\lib\rt.jar;D:\Users\bobshute\IdeaProjects\out\production\jvmStu" AllStackTraces
线程:Monitor Ctrl-Break
java.net.SocketInputStream.socketRead0(Native Method)
java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
java.net.SocketInputStream.read(SocketInputStream.java:170)
java.net.SocketInputStream.read(SocketInputStream.java:141)
sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
java.io.InputStreamReader.read(InputStreamReader.java:184)
java.io.BufferedReader.fill(BufferedReader.java:161)
java.io.BufferedReader.readLine(BufferedReader.java:324)
java.io.BufferedReader.readLine(BufferedReader.java:389)
com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)
线程:Attach Listener
线程:Signal Dispatcher
线程:Finalizer
java.lang.Object.wait(Native Method)
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
线程:Reference Handler
java.lang.Object.wait(Native Method)
java.lang.Object.wait(Object.java:502)
java.lang.ref.Reference.tryHandlePending(Reference.java:191)
java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

Process finished with exit code 0

8.4 操作示例

1.top查找出哪个进程消耗的cpu高。执行top命令,默认是进程视图,其中PID是进程号
co_ad2    18   0 1817m 776m 9712 S  3.3  4.9  12:03.24 java                                                                                           
co_ad     21   0 3028m 2.5g 9432 S  1.0 16.3   6629:44 ja


这里我们分析21125这个java进程
2.top中shift+h 或“H”查找出哪个线程消耗的cpu高 
先输入top,然后再按shift+h 或“H”,此时打开的是线程视图,pid为线程号
co_ad2    15   0 1807m 630m 9492 S  1.3  4.0   0:05.12 java                                                                                           
co_ad2_s  15   0 1360m 560m 9176 S  0.3  3.6   0:46.72 java                                                                                           

这里我们分析21233这个线程,并且注意的是,这个线程是属于21125这个进程的。 

3.使用jstack命令输出这一时刻的线程栈,保存到文件,命名为jstack.log。注意:输出线程栈和保存top命令快照尽量同时进行。
  由于jstack.log文件记录的线程ID是16进制,需要将top命令展示的线程号转换为16进制。

4. jstack查找这个线程的信息 
jstack [进程]|grep -A 10 [线程的16进制] 
即: jstack 21125|grep -A 10 52f1  

-A 10表示查找到所在行的后10行。21233用计算器转换为16进制52f1,注意字母是小写。 
结果: 

"http-8081-11" daemon prio=10 tid=0x00002aab049a1800 nid=0x52bb in Object.wait() [0x0000000042c75000]  
   java.lang.Thread.State: WAITING (on object monitor)  
     at java.lang.Object.wait(Native Method)  
     at java.lang.Object.wait(Object.java:485)  
     at org.apache.tomcat.util.net.JIoEndpoint$Worker.await(JIoEndpoint.java:416)  

在结果中查找52f1,可看到当前线程在做什么。

8.5参考文章

java命令–jstack 工具

9 可视化虚拟机工具-Jconsole

9.1 简介

从Java 5开始 引入了 JConsole。JConsole 是一个内置 Java 性能分析器,可以从命令行或在 GUI shell 中运行。您可以轻松地使用 JConsole(或者,它更高端的 “近亲” VisualVM )来监控 Java 应用程序性能和跟踪 Java 中的代码。

如果考虑到安全因素,需要认证,需要安全连接,也是可以搞定的。参考:http://download.oracle.com/javase/6/docs/technotes/guides/management/agent.html#gdenv

9.1 Jconsole内存监控

bin目录下jconsole选打开后选择线程即可查看内存情况.

cmd->jconsole->选择线程

9.2 Jconsole线程监控

package jconsole;

import sun.applet.Main;

import java.util.Scanner;

public class TestJconsole {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        sc.next();//控制台输入

        testWhileTrue();

        sc.next();//再次输入

        testWait(new Object());
    }


   //Runnable的状态
    private static void testWhileTrue() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                //cpu 占用率高
                while(true){

                }
            }
        },"while true").start();
    }


    //等待的状态
    private static void testWait(Object obj) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (obj){
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        },"wait").start();
    }

    /**
     1.执行完main通过jconsole查看线程情况

     名称: main
     状态: RUNNABLE
     总阻止数: 0, 总等待数: 0

     堆栈跟踪:
     java.io.FileInputStream.readBytes(Native Method)
     java.io.FileInputStream.read(FileInputStream.java:255)
     java.io.BufferedInputStream.read1(BufferedInputStream.java:284)
     java.io.BufferedInputStream.read(BufferedInputStream.java:345)
     - 已锁定 java.io.BufferedInputStream@429ba590
     sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
     sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
     sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
     - 已锁定 java.io.InputStreamReader@bd5bea1
     java.io.InputStreamReader.read(InputStreamReader.java:184)
     java.io.Reader.read(Reader.java:100)
     java.util.Scanner.readInput(Scanner.java:804)
     java.util.Scanner.next(Scanner.java:1369)
     jconsole.TestJconsole.main(TestJconsole.java:11)


     2.输入一些字符后出现了while true线程,通过jconsole查看线程情况

     名称: while true
     状态: RUNNABLE
     总阻止数: 0, 总等待数: 0

     堆栈跟踪:
     jconsole.TestJconsole$2.run(TestJconsole.java:41)
     java.lang.Thread.run(Thread.java:745)


     3.在输入一些字符后出现了 wait线程,通过jconsole查看线程情况

     名称: wait
     状态: java.lang.Object@31ca3217上的WAITING
     总阻止数: 0, 总等待数: 1

     堆栈跟踪:
     java.lang.Object.wait(Native Method)
     java.lang.Object.wait(Object.java:502)
     jconsole.TestJconsole$1.run(TestJconsole.java:28)
     java.lang.Thread.run(Thread.java:745)

     */

}

9.3 Jconsole线程死锁监控

资源1,2,线程A,B;A要用1锁1然后要用2,B要用2锁2然后要用1,然后同时进行,都不释放,都等待.造成死锁.

package jconsole.deatlock;

public class DeadLock implements  Runnable {

    private Object obj1;
    private Object obj2;

    public DeadLock(Object obj1,Object obj2){
        this.obj1 = obj1;
        this.obj2 = obj2;
    }

    @Override
    public void run() {
        synchronized (obj1){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (obj2){
                System.out.println("run core");
            }
        }
    }
}





package jconsole.deatlock;

public class TestDeadLock {
    public static void main(String[] args) {
        Object obj1 = new Object();
        Object obj2 = new Object();

        new Thread(new DeadLock(obj1,obj2),"thread1").start();
        new Thread(new DeadLock(obj2,obj1),"thread1").start();
    }

    /**
     通过jconsole可以查看如下连个线程:

     名称: Thread-0
     状态: java.lang.Object@50649ae7上的BLOCKED, 拥有者: Thread-1
     总阻止数: 1, 总等待数: 1

     堆栈跟踪:
     jconsole.deatlock.DeadLock.run(DeadLock.java:22)
     - 已锁定 java.lang.Object@5e76a81d
     java.lang.Thread.run(Thread.java:745)


     名称: Thread-1
     状态: java.lang.Object@5e76a81d上的BLOCKED, 拥有者: Thread-0
     总阻止数: 1, 总等待数: 1

     堆栈跟踪:
     jconsole.deatlock.DeadLock.run(DeadLock.java:22)
     - 已锁定 java.lang.Object@50649ae7
     java.lang.Thread.run(Thread.java:745)


     */
}

9.4 使用介绍

9.4.1 StartPage

JconsoleJconsole-1-StartPage

概述: Displays overview information about the Java VM and monitored values.
内存: 显示内存使用信息
线程: 显示线程使用信息
类: 显示类装载信息
VM摘要:显示java VM信息
MBeans: 显示 MBeans

9.4.2 内存

Jconsole-2-内存.png

这个比较有价值,参看堆内存,非堆内存,内存池的状况总体内存的分配和使用情况以及不同的GC进行垃圾回收的次数和时间。可以手动进行GC查看内存变化。

GC的算法和参数对性能有显著的影响,注意垃圾回收次数、时间、以及partial GC和full GC,调整你所使用的不同GC和以及各个GC下的参数,然后在这个视图下观察,以得到好的性能。

9.4.3 线程

Jconsole-3-线程.png

左下角显示所有的活动线程(如果线程过多,可以在下面的过滤栏中输入字符串过滤出你想要观察的线程)。点击某个显示会显示这个线程的名称、状态、阻塞和等待的次数、堆栈的信息。

统计图显示的是线程数目的峰值(红色)和当前活动的线程(蓝色)。

另外下面有个按钮“检测到死锁”。

9.4.4 类

Jconsole-4-类.png

9.4.5 VM概要

Jconsole-5-VM概要.png

9.4.6 MBean

Jconsole-6-MBean.png

10. VisualVM使用详解

VisualVM首页

VisualVM插件地址

还可以安装插件.

VisualVM-1
VisualVM-2
VisualVM-3
VisualVM-4
VisualVM-5

11.JVM调优

11.1概述

  • 知识
  • 工具
  • 数据
  • 经验

11.2 案例1-大对象

卡顿现象:大对象,停顿时间长,总是fullGC.
原因:每个用户登陆的时候产生一个大大的对象,大对象直接分配到老年代.登陆多大对象多,造成老年代不足,造成fullGC(因为minorGC不能解决问题),然后由于堆内存大,造成FullGC时间长.

内存分配策略:
- 优先分配到Eden
- 大对象直接分配到老年代
- 长期存活的对象分配到老年代
- 空间分配担保
空间不足时到老年代去借空间
- 动态对象年龄判断

解决方法:一台拆成多台,每台减少对内存.(不够好)

11.3 案例2-物理内存(非JVM)不足

现象:不定期内存溢出,加大对内存也无济于事,导出堆快照信息,没有任何信息.内存监控正常.

原因:NIO为了提高性能,申请堆外内存,需要占用虚拟机之外的机器物理内存.

注意:其它如元空间等占用非JVM而需要物理内存的情况.

12 半总结

  • 原理 + 工具 + 案例
  • Java虚拟机的运行时区域
    • 线程独占区 栈,程序计数器,本地方法栈
    • 线程共享区 堆,方法区
  • 对象的创建和回收
    • 垃圾对象的标记算法
      • 引用计数法
      • 可达性分析法
        – 垃圾收集算法
      • 标记-清除算法
        比较慢,
      • 复制算法
        新生代
      • 标记整理算法
        老年代
      • 分代收集算法
    • 垃圾收集器 serial parnew parallel cms g1
  • 对像内存分配原则

    • 对象首先在Eden分配
    • 大对象直接进入到老年代
    • 长期存活的对象进入到老年代
    • 空间分配担保
    • 逃逸分析以及栈上分配
  • 工具
    • 命令行工具 jps jstat jinfo jmap jhat jstart
    • 图形化工具 jConsole VisualVM
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值