ITPub博客

首页 > 数据库 > Oracle > Oracle的awr报告分析

Oracle的awr报告分析

原创 Oracle 作者:xz43 时间:2020-09-18 16:12:11 0 删除 编辑

CPU的使用情况是AWR报告的一个重要指标,接下来,首先了解DB Time 与 DB CPU的区别。

一、重要参数介绍

DB Time Amount of elapsed time (in microseconds) spent performing Database user-level calls. This does not include the elapsed time spent on instance background processes such as PMON.

DB TIME:所有前台用户级调用(foreground user-level calls)花费在database上执行的时间总和,包括CPU time、IO time、cpu on queue time和其他一系列非空闲等待时间,但不包括后台进程background processes执行时间。


DB CPU Amount of CPU time (in microseconds) spent on database user-level calls. This does not include the CPU time spent on instance background processes such as PMON.

DB CPU:所有前台用户级调用(foreground user-level calls)database时CPU服务时间总和,不包括实例后台进程 background processes执行时间。

这里写图片描述

公式:DB TIME= DB CPU Time + DB Wait Time


CPU和等待时 间调整维度

调整系统时,将CPU时间(DB CPU Time)与系统的等待时间(DB Wait Time)进行比较很重要。通过比较CPU时间与等待时间,您可以确定有多少时间是花在有用的工作,有多少时间花在等待其他进程可能持有的资源上。作为一般规则,CPU时间占优势的系统通常比等待时间占优势的系统需要更少的调整。但是,CPU使用量过大可能是由严重的SQL语句引起的。等待时间到CPU时间的比例总是趋于随着系统负载的增加而增加,等待时间的急剧增加是资源争用的征兆,并且必须寻求良好的可扩展性。

这里写图片描述

当等待时间(DB Wait Time)的增加表明资源争用趋于严重,向节点添加更多的CPU来提高性能,收效甚微。相反,随着负载增加,CPU时间的比例不会显著下降的系统可以更好地扩展,并且最有可能从添加CPU或实际应用集群(RAC)实例中受益。

注意:如果CPU时间部分是前五名事件之一,则自动工作负载存储库(AWR)和Statspack报告显示CPU时间以及前5个事件部分中的等待时间。


二、AWR分析介绍

由于公司限制,只能借用网络上的图进行分析。

2.1、AWR头部信息

WORKLOAD REPOSITORY report for

DB Name DB Id Instance Inst num Startup Time Release RAC
DB11G 1852520651 db11g 1 10-Aug-20 10:08 11.2.0.3.0 NO

Host Name Platform CPUs Cores Sockets Memory (GB)
c201998 Linux x86 64-bit 128 64 4 504.64

Snap Id Snap Time Sessions Cursors/Session
Begin Snap: 19805 21-Aug-20 15:00:55 1538 1.8
End Snap: 19809 21-Aug-20 16:00:09 2587 2.2
Elapsed:
59.23 (mins)

DB Time:
8,342.46 (mins)

从上图可知,该awr指定的前后2个snapshot时间间隔为59.23mins,DB层调用花费的时间为8342.46mins

当前数据库的逻辑CPU为128个,因此每CPU平均服务时间为 8342.46/128=65.175mins

按前面DB Time的描述,DB Time = DB Wait Time + DB CPU Time ,其中每CPU的 DB CPU 为 59.23mins,可推出DB Wait Time 约为 6mins

2.2、Load Profile 

Per Second Per Transaction Per Exec Per Call
DB Time(s): 140.9 0.0 0.00 0.00
DB CPU(s): 45.9 0.0 0.00 0.00
Redo size: 49,914,623.2 11,182.9

Logical reads: 6,610,588.0 1,481.0

Block changes: 319,439.7 71.6

Physical reads: 8,297.9 1.9

Physical writes: 11,500.2 2.6

User calls: 50,785.4 11.4

Parses: 49,796.1 11.2

Hard parses: 587.2 0.1

W/A MB processed: 627.1 0.1

Logons: 12.7 0.0

Executes: 222,018.5 49.7

Rollbacks: 851.2 0.2

Transactions: 4,463.5


由上图可知:

DB Time(s)          每一个自然时间秒 DB Time 对应为140.9s,据此推算140.9*59.23*60/60 约等于头部的 DB Time 8345.507mins ;

DB CPU(s)          每一个自然时间秒 CPU的开销为45.9s,即45.9*59.23*60/60=2718.657mins,也就是说花在CPU上的时间约为 2719mins;

Redo size           每秒产生的redo(单位 byte),如上大约为49M,日志量很大。而 大的redo size往往对lgwr写日志,和arch归档造成I/O压力,结合Per Transaction可以用来分辨是大量小事务,还是小量大事务,根据上面的Per Transactions可知这里应该是大量的小事务;

Logical reads     每秒事务逻辑读的大小(单位 ),值大的话一般耗cpu,并且伴随latch: cache buffer chains等待事件;

Block changes    描述数据块的变化(单位 );

Physical reads    前台session的花费 (单位 ,并不是一个衡量IO的指标,IO负载一般参考 Instance Activity Stats 中的:

  • 每秒physical read total bytes + 每秒physical write total bytes < 磁盘传输速率(吞吐量)即可

  • 每秒physical read total IO requests + 每秒physical write total IO requests < 磁盘IOPS 即可

  • DB总的物理吞吐量/秒 = physical read total bytes + physical write total bytes

  • DB总的物理IOPS= physical read total IO requests + physical write total IO requests

Physical writes    前台session的花费 (单位 ,并不是一个衡量IO的指标,IO负载一般参考 Instance Activity Stats中的

  • 每秒physical read total bytes + 每秒physical write total bytes < 磁盘传输速率(吞吐量)即可

  • 每秒physical read total IO requests + 每秒physical write total IO requests < 磁盘IOPS即可

User calls            每秒事务call的次数,Oracle中的operation都是由多个call来组成,例如一个游标操作由五个阶段open、 parse、 exec、 fetch、close,所以这个游标就需要 5 个call;

Parses                每秒事务的SQL语句解析的次数,包括软解析+硬解析,parses超过每秒300表明可能有争用问题。(全部都是软解析也不一定好,软解析也要到 share pool 中查找共享的执行计划,这样就避免不了一些 latch,也会对cpu造成压力,最好的软软解析就是不解析。软软解析两种方法,第一种方法:设置了session_cached_cursors参数时,当某个session第三次执行相同的SQL语句时,则会把该SQL语句的游标信息转移到该session的PGA中。这样,当该session在执行该SQL语句时,会直接从PGA中取出执行计划,从而跳过所有解析的步骤。第二种方法:写程序的时候使用静态游标,将解析移到循环之外,就是一次解析,多次执行。declare xx cursor is …begin loop ..end loop end;)

Hard parses        每秒事务硬解析的次数,如果硬解析次数太高,说明SQL重用率不高。 避免硬解析的方法是尽可能的使用绑定变量,Hard parses大于每秒100表明可能有争用问题;

Logons               每秒/每事务登录的次数,oracle虽然没类似sqlserver一样的连接池的概念,但是oracle的session总量(就是登陆连接总数)是受参数控制的,是有限的珍贵资源,所以我们一般登陆执行完毕后就及时退出以便释放session数量,但是我们也要考虑不能太快退出(太快退出就是短连接),因为短连接情况下新建立的会话没有缓存游标信息,进而导致无法避免大量的软解析,长连接就能够使用PGA软软解析,而且建立连接(connection)同样会消耗大量的CPU TIME(长连接:Client方与Server方先建立通讯连接,连接建立后不断开,然后再进行报文发送和接收。 短连接:Client方与Server每进行一次报文收发交易时才进行通讯连 接,交易完毕后立即断开连接);

Executes            每秒事务SQL执行次数,包括用户执行的sql语句与系统执行的sql语句,表示一个系统sql的繁忙程度,这个系统非常忙;

Rollbacks           回滚次数,反应回滚频率,但是这个指标不太精确,平时很少看Rollbacks这个指标;

Transactions     每秒产生的事物个数,反映数据库负载程度,上面每秒事务数 4463.5 ,负载很高;

2.3、 Top 5 Timed Foreground Events 

    Event Waits Time(s) Avg wait (ms) % DB time Wait Class
    DB CPU
    163,053
    32.57
    db file sequential read 25,206,730 86,643 3 17.31 User I/O
    log file sync 9,338,653 80,956 9 16.17 Commit
    library cache lock 1,260 67,080 53238 13.40 Concurrency
    read by other session 15,968,733 47,001 3 9.39 User I/O

    上图为Top 5 等待事件,总体来说,数据库等待时间较长。

    db file sequential read 等待时间为 86643s,平均等待3ms,占据了整个DB Time 的17.31% ;

    将上述除DB CPU外的时间加总,总和为281680s,约为Load Profile计算出来的337611s((140.9-45.9)*59.23*60) 的83%,说明以上几个等待事件占整个等待时间的83%。

    通过上面的计算可知当前的数据库非空闲等待较为严重。

    log file sync 可以看到,引起该等待事件的主要原因是系统频繁的 Commit ,一般会同时出现LGWR后端进程的 log file parallel write 等待事件;

    library cache lock 可以看到,引起该等待事件的主要原因是并发Concurrency,对Library Cach的争用造成的;具体解释请查看后面的附三部分;

    db file sequential read read by other session 两个等待事件主要是因为User I/O引起,具体参考下面的 Foreground Wait Events详细解释;


    2.4、 Host CPU & Instance CPU

    Host CPU (CPUs: 128 Cores: 64 Sockets: 4)

    Load Average Begin Load Average End %User %System %WIO %Idle
    38.93 24.35 34.3 3.2 8.4 62.1

    Instance CPU

    %Total CPU %Busy CPU %DB time waiting for CPU (Resource Manager)
    36.5 96.2 0.0

    上图为主机CPU负载情况。 

    主机CPU 

    主机CPU主要有User%,Sys%,Idle%, 在上面的报告中,“Load Average” begin/end值代表每个CPU的大致运行队列大小,主机负载呈现下降趋势,由38.93降到24.35 

    上面 %User = 34.3 , %System = 3.2 ,%Idle = 62.1 所以 %Busy = 100 - 62.1 = 37.9 

    %WIO表示CPU等待IO占比,此处为8.4%,也就是说当前的系统IO存在瓶颈

    实例CPU 

    如上,oracle 实例CPU占主机CPU的36.5%,即 %Total CPU 为 36.5

    %Total CPU,该实例所使用的CPU占总CPU的比例—>% of total CPU for Instance 

    %Busy CPU,该实例所使用的Cpu占总的被使用CPU的比例—> % of busy CPU for Instance 

    %DB time waiting for CPU (Resource Manager) 是指当使用了resource manager限制某个用户和会话使用CPU,而产生的等待。会产生resmgr:cpu quantum等待事件,如果产生该等待事件需要和RSRC_MGR的值结合起来判断。解决方法是需要修改资源限制的plan。 

    以下计算结合了操作系统统计信息,数据见后面截图

    % of busy CPU for Instance= %Total CPU/(%Busy) * 100 = 36.5/37.9*100= 96.3%  和报告里的96.2相吻合;

    % of Total CPU for Instance = ( DB CPU+ background cpu time)/( (BUSY_TIME+IDLE_TIME)/100) * 100 = (163,052.73 + 2,100.59)/ ((17,167,974 +28,119,561) /100) * 100 = 36.47% 和报告里的36.5相吻合; 

    %DB time waiting for CPU (Resource Manager)= (RSRC_MGR_CPU_WAIT_TIME/100)/DB TIME

    2.5、 Time Model Statistics

    Time Model Statistics

    • Total time in database user-calls (DB Time): 500547.8s
    • Statistics including the word "background" measure background process time, and so do not contribute to the DB time statistic
    • Ordered by % or DB time desc, Statistic name
    Statistic Name Time (s) % of DB Time
    sql execute elapsed time 405,916.91 81.09
    DB CPU 163,052.73 32.57
    parse time elapsed 40,993.56 8.19
    hard parse elapsed time 16,786.01 3.35
    PL/SQL execution elapsed time 15,033.44 3.00
    repeated bind elapsed time 474.08 0.09
    hard parse (sharing criteria) elapsed time 372.41 0.07
    connection management call elapsed time 259.04 0.05
    sequence load elapsed time 158.20 0.03
    failed parse elapsed time 101.18 0.02
    hard parse (bind mismatch) elapsed time 91.71 0.02
    PL/SQL compilation elapsed time 8.73 0.00
    inbound PL/SQL rpc elapsed time 7.51 0.00
    DB time 500,547.81
    background elapsed time 23,670.92
    background cpu time 2,100.59

    上图为时间统计模型。 

    sql execute elpased time 时间占主导,即时间耗用主要是在SQL执行上面。 

    这些SQL的执行对应的等待事件见前面的Top Event,也就是说等待和争用比较突出。 

    注意:该 时间模型中的指标 存在包含关系,所以存在Time Model Statistics加起来超过100%情形。

    2.6、 Operating System Statistics

    Operating System Statistics

    • *TIME statistic values are diffed. All others display actual values. End Value is displayed if different
    • ordered by statistic type (CPU Use, Virtual Memory, Hardware Config), Name
    Statistic Value End Value
    BUSY_TIME 17,167,974
    IDLE_TIME 28,119,561
    IOWAIT_TIME 3,820,591
    NICE_TIME 0
    SYS_TIME 1,459,556
    USER_TIME 15,514,392
    LOAD 39 24
    PHYSICAL_MEMORY_BYTES 541,851,877,376
    NUM_CPUS 128
    NUM_CPU_CORES 64
    NUM_CPU_SOCKETS 4
    GLOBAL_RECEIVE_SIZE_MAX 4,194,304
    GLOBAL_SEND_SIZE_MAX 1,048,576
    TCP_RECEIVE_SIZE_DEFAULT 87,380
    TCP_RECEIVE_SIZE_MAX 4,194,304
    TCP_RECEIVE_SIZE_MIN 4,096
    TCP_SEND_SIZE_DEFAULT 16,384
    TCP_SEND_SIZE_MAX 4,194,304
    TCP_SEND_SIZE_MIN 4,096

    以上为操作系统统计信息。IO等待的时间为35287厘秒(单位: 百分之一秒);

    2.7、 Foreground Wait Events

    Foreground Wait Events

    • s - second, ms - millisecond - 1000th of a second
    • Only events with Total Wait Time (s) >= .001 are shown
    • ordered by wait time desc, waits desc (idle events last)
    • %Timeouts: value of 0 indicates value was < .5%. Value of null is truly 0
    Event Waits %Time -outs Total Wait Time (s) Avg wait (ms) Waits /txn % DB time
    db file sequential read 25,206,730 0 86,643 3 1.59 17.31
    log file sync 9,338,653 0 80,956 9 0.59 16.17
    library cache lock 1,260 0 67,080 53238 0.00 13.40
    read by other session 15,968,733 0 47,001 3 1.01 9.39
    library cache: mutex X 2,134,852 0 28,375 13 0.13 5.67
    enq: TX - row lock contention 624,522 0 17,990 29 0.04 3.59

    以上,仅仅罗列top6的Foreground Wait Events。

    db file sequential read     是个非常常见的 I/O 相关的等待事件,通常显示与单个数据块相关的读取操作。在大多数的情况下,读取一个 索引块或者通过索引读取一个数据块时,都会记录这个等待。这个等待事件有 3 个参数 P1、 P2、 P3,其中 P1 代表 Oracle 要读取的文件的绝对文件号, P2 代表 Oracle 从这个文件中开始读取的起始数据块块号, P3 代表读取的 BLOCK数量,通常这个值为 1,表明是单个 BLOCK 被读取。db file sequential read 等待时间是由于执行对索引、回滚(undo)段和表(当借助rowid来访问)、控制文件和数据文件头的单块读操作SQL语句(用户和递归)引起的。对于这些对象的物理I/O请求是很正常的,因此db file sequential read等待的存在不是一定意味库或应用出错了。 

    如果这个等待事件比较显著,可能表示在多表连接中,表的连接顺序存在问题,没有正确地使用驱动表;或者可能索引的使用存在问题,并非索引总是最好的选择。

    db file sequential read 的优化方法:

        1)从读取开始,增加SGA中buffer cache的大小,避免每次都从硬盘中去读数;

        2)优化sql语句,减少不必要的块读取;

    log file sync                 当一个用户提交(commits)或者回滚(rollback)时,session的redo信息需要写出到redo logfile中。用户进程将通知LGWR执行写出操作,LGWR完成任务以后会通知用户进程,这个等待事件就是指用户进程等待LGWR的写完成通知。对于回滚操作,该事件记录从用户发出rollback命令到回滚完成的时间。如果该等待过多,可能说明LGWR的写出效率低下,或者系统提交过于频繁。针对该问题,可以关注 log file parallel write 等待事件,通过 user commits,user rollback等统计信息可以用于观察提交或回滚次数。

    log file sync 的优化方法:

        1)提高LGWR性能,尽量使用快速磁盘,不要把redo log file 存放在raid 5的磁盘上;

        2)应用尽量使用批量提交,对于已有应用,改造工作量会比较大,而且很多场景未必适应;

        3)根据业务场景,适当使用NOLOGGING/UNRECOVERABLE等选项配合使用;

    library cache lock          该等待事件可以参考下面的附三;

    read by other session     等待事件其实是oracle IO问题一个比较尴尬的场景,其原理是多个会话并发将同一数据块从磁盘读入SGA的data buffer cache中 ,但ORACLE同一时间只允许一个会话从磁盘将同一数据块读入SGA,在并发情况下其它session必须等待,因此就有了read by other session等待事件,结合 db file sequential read 这个等待事件原理,基本上可以断定,系统发生了高并发查询,并且从log file sync等待事件来看,系统发生了频率的提交操作,导致数据库日志频繁同步。需要查找AWR报告中跟I/O有关的SQL语句,进行优化。

    library cache: mutex X      在Oracle11g中,mutex X替代了之前的library cache latch,主要作用是在hash bucket中定位handle时使用(比如SQL 硬解析时,需要往hash bucket中新增一个cursor时,需要library cache latch) mutex是11g中采用的轻量级锁,替代latch(更轻量,更细粒度)。其中mutex X表示eXclusive模式,如果是mutex s则是共享模式。

    Library cache: mutex 等待事件产生常见原因:

    • os资源不足(cpu、内存)

    • sga设置不合理,shared_pool不足,或动态调整导致hard parse

    • hot object contention

    • hard parse

    • sql high version count 由于子游标太多,扫描时会形成锁

    • library cache object失效导致重编译

    • bug

    enq: TX - row lock contention     通常是Application级别的问题。其中的enq是enquence的简写(enquence是协调访问数据库资源的内部锁)。所有以“enq:”打头的等待事件都表示这个会话正在等待另一个会话持有的内部锁释放,它的名称格式是 enq:enqueue_type - related_details。这里的enqueue_type是TX,related_details是row lock contention。数据库动态性能视图v$event_name提供所有以“enq:”开头的等待事件的列表。

    enq: TX - row lock contention 等待事件产生的三种情况如下:

    • 第一种情况,是真正的业务逻辑上的行锁冲突,如一条记录被多个人同时修改。这种锁对应的请求模式是6(Waits for TX in mode 6 :会话A持有row level lock,会话B等待这个lock释放)。不同的session更新或删除同一个记录。(This occurs when one application is updating or deleting a row that another session is also trying to update or delete. )

    • 第二种情况,是唯一键冲突(In mode 4,唯一索引),如主键字段相同的多条记录同时插入。这种锁对应的请求模式是4。这也是应用逻辑问题。表上存在唯一索引,会话A插入一个值(未提交),会话B随后也插入同样的值;会话A提交后,enq: TX - row lock contention消失。

    • 第三种情况,是bitmap索引的更新冲突(in mode 4 :bitmap),就是多个会话同时更新bitmap索引的同一个数据块。源于bitmap的特性(位图索引的一个键值,会指向多行记录,所以更新一行就会把该键值指向的所有行锁定)。此时会话请求锁的对应的请求模式是4。bitmap索引的物理结构和普通索引一样,也是 B-tree 结构,但它存储的数据记录的逻辑结构为"key_value,start_rowid,end_rowid,bitmap"。



    三、附上Oracle的SQL解析过程介绍

    每一个sql被执行之前,会先将sql语句经过ascii转换,通过hash算法获得对应的hash_value, 根据hash_value先到library cache的hash buckets 中查找parent cursor,这就需要先获得library cache latch;找到parent cursor后,就会去查找对应的child cursor,当发现无法找到时,就会释放library cache latch,获得share pool latch分配空间给硬解析后所产生的执行计划;然后再次获得library cache latch并把新生成的执行计划放入share pool,转入library cache pin+lock(null模式)开始执行sql。library cache latch 的个数有限(与CPU_COUNT参数相关),当数据库中出现大量硬解析的时候,某一个sql无法得到library cache latch就会开始spin,达到spin count后还没得到,就会开始sleep,达到sleep时间后醒来,再次试图获得library cache latch,在spin得不到,则继续sleep…依此类推。通过持有 library cache latch获得父游标里面包含的sql文本,随后生成子游标包含执行计划和解析树,执行计划在heap 6中,解析完毕之后sql开始执行,最终反馈结果给客户端。

    下图是经典的library cache结构图。

    在这里插入图片描述

    我们可以看到在每一个library cache object handle上都存在4个链表,它们分别是lock owners,lock waiters,Pin owners,Pin waiters。所以实际上library cache object handle你可以把它看成是library cache lock和library cache pin所保护的resource。而向lock owners和lock waiters里添加/修改一个library cache lock时是需要先持有Child library cache lock;向Pin owners和Pin waiters里添加/修改一个library cache pin时是需要先持有Child library cache pin。

    • 硬解析 :library cache 中不存在父游标,或者存在父游标但是没有子游标;

    • 软解析 :library cache 中存在父游标和子游标;

    • 软软解析 :会话pga中存在session cursor;

    无论是硬解析还是软解析,在扫描/修改 library cache object handle 链表的时候是需要先持有 Child library cache;另外,硬解析还涉及到要在shared pool 里分配内存,所以硬解析还会持有 Child shared pool。


    下面,将逐个解释各种解析的原理及举例说明。

    硬解析过程:

    1.SQL的语法、语义及权限检查;

    2.查询转换(通过应用各种不同的转换技巧,会生成语义上等同的新的SQL语句,如count(1)会转为count(*));

    3.根据统计信息生成执行计划(根据CBO/LBO算法,找出成本最低的路径,这一步比较耗时);

    4.将游标信息(执行计划)保存到库缓存 library cache。

    软解析过程:

    1.SQL的语法、语义及权限检查;

    2.将整条SQL hash后从库缓存library cache中查找对应的执行计划。

    软解析对比硬解析省了三个步骤。

    软软解析过程:

    要完全理解软软解析先要理解游标的概念,当执行SQL时,首先要打开游标,执行完成后,要关闭游标,游标可以理解为SQL语句的一个句柄。

    在执行软软解析之前,首先要进行软解析,MOS上说执行3次的SQL语句会把游标缓存到PGA,这个游标一直开着,当再有相同的SQL执行时,则跳过解析的所有过程直接去取执行计划。


    解析演示举例

    SQL> drop table test purge;
    SQL> alter system flush shared_pool;
    SQL> create table test as select * from dba_objects where 1<>1;
    SQL> exec dbms_stats.gather_table_stats(user,'test');

    硬解析:
    SQL> select * from test where object_id=20;
    未选定行
    SQL> select * from test where object_id=30;
    未选定行
    SQL> select * from test where object_id=40;
    未选定行
    SQL> select * from test where object_id=50;
    未选定行

    软解析:
    SQL> var oid number;
    SQL> exec :oid:=20;
    SQL> select * from test where object_id=:oid;
    未选定行
    SQL> exec :oid:=30;
    SQL> select * from test where object_id=:oid;
    未选定行
    SQL> exec :oid:=40;
    SQL> select * from test where object_id=:oid;
    未选定行
    SQL> exec :oid:=50;
    SQL> select * from test where object_id=:oid;
    未选定行

    软软解析:
    SQL> begin
    for i in 1..4 loop
    execute immediate 'select * from test where object_id=:i' using i;
    end loop;
    end;
    /

    SQL> col sql_text format a40
    SQL> select sql_text,s.PARSE_CALLS,loads,executions from v$sql s
    where sql_text like 'select * from test where object_id%'
    order by 1,2,3,4;
    SQL_TEXT                         PARSE_CALLS     LOADS     EXECUTIONS
    ---------------------------------------- ----------- ---------- ----------
    select * from test where object_id=20         1     1     1
    select * from test where object_id=30         1     1     1
    select * from test where object_id=40         1     1     1
    select * from test where object_id=50         1     1     1
    select * from test where object_id=:i           1     1     4
    select * from test where object_id=:oid       4     1     4

    可以看到软解析与软软解析相比,软软解析只是解析一次。

    字段解释:
    PARSE_CALLS 解析的次数;

    LOADS 硬解析的次数;

    EXECUTIONS 执行的次数;

    来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/9399028/viewspace-2716789/,如需转载,请注明出处,否则将追究法律责任。

    上一篇: JDK12新特性详解
    请登录后发表评论 登录
    全部评论
    鱼儿的学习空间

    注册时间:2010-11-16

    • 博文量
      424
    • 访问量
      1757541