1. Top命令:当前负载机cpu 占用情况,及详解
第一行top | 05:34:05 | up 153days | 0 users | load average: 0.85,0.78,0.66 | |||||
---|---|---|---|---|---|---|---|---|---|
任务队列 | 表示当前时间 | 系统运行时间 | 当前登录用户数 | 系统负载,即任务队列的平均长度。 三个数值分别为 1分钟、5分钟、15分钟前到现在的平均值 | |||||
第二行Tasks: | 38 totals | 1 running | 34 sleeping | 3 stopped | 0 zombie | ||||
进程 | 进程总数 | 正在运行的进程数 | 睡眠的进程数 | 停止的进程数 | 僵尸进程数 | ||||
第三行%Cpu(s) | 7.0 us | 4.7 sy | 0.0 ni | 87.5 id | 0.0 wa | 0.8 hi | 0.0 si | ||
CPU的信息 | 用户空间占用CPU百分比 | 内核空间占用CPU百分比 | 用户进程空间内改变过优先级的进程占用CPU百分比 | 空闲CPU百分比 | 等待输入输出的CPU时间百分比 | 硬中断(Hardware IRQ)占用CPU的百分比 | 软中断(Software Interrupts)占用CPU的百分比 | ||
第四行 KiB Mem: | 32626.0 total | 2252.2 free | 12445.7 used | 17938.1 buff/cache | |||||
内存信息 | 物理内存总量 | 使用的物理内存总量 | 空闲内存总量 | 用作内核缓存的内存量 | |||||
第五行 KiB Swap | 32767.9 total | 32439.9.2 free | 328.0 used | 15132.6 buff/cache | |||||
交换区总量 | 使用的交换区总量 | 空闲交换区总量 | 代表可用于进程下一次分配的物理内存数量 | ||||||
进程信息 | PID | USER | NI | VIRT | RES | SHR | S | %CPU | %MEM |
具体进程信息 | 进程id | 进程所有者的用户名 | nice值。负值表示高优先级,正值表示低优先级 | 进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES | 进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA | 共享内存大小,单位kb | 进程状态。D=不可中断的睡眠状态 R=运行 S=睡眠 T=跟踪/停止 Z=僵尸进程 | 该进程占用的 CPU 使用率 | 该进程占用的物理内存和总内存的百分比 |
2. JPS命令:查询当前进程资源
jps -l #查询所有java进程
jps -l | grep 进程ID # 根据进程ID进行过滤
3.JSTAT 命令:查看GC垃圾回收统计情况
jstat -gc 7 2000 100 # 每个2秒打印一次 打印100次
jstat -gcutil 7 2000 10 # 查询当前进程下,各区已使用的内存占比情况
jstat -gccause 7 # 近一次GC统计和原因
程序的运行产生的对象在Eden区增长。
当Eden区的EU达到了EC(最大容量)时,这个时候就会进行Eden区的垃圾回收,依然存活的对象会从Eden区转移到S0区;
当对象持续增长时,就会执行S0的垃圾回收,依然存活的对象会从S0转移到S1,如果对象依旧持续产生,就会有对象转移到Old区,也就是老年代。
当老年代的内存容量也满了时,就会发生Full GC。
S0C | S1C | S0U | S1U | EC | EU | OC | OU | MC | MU | CCSC | CCSU | YGC | YGCT | FGC | FGCT | GCT |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
第一个幸存区的大小 | 第二个幸存区的大小 | 第一个幸存区的使用大小 | 第二个幸存区的使用大小 | 伊甸园区的大小 | 伊甸园区的使用大小 | 老年代大小 | 老年代使用大小 | 方法区大小 | 方法区使用大小 | 压缩类空间大小 | 压缩类空间使用大小 | Minor GC次数 | Minor GC耗时 | Full GC次数 | 老年代垃圾回收消耗时间 | Minor & Full GC共计耗时 |
4.JMAP命令:查看JVM信息
bash: jmap: command not found
命令找不到
yum -y install java-devel
4.1 查看整个JVM内存状态
jmap -heap PID
# 堆的配置(Heap Configuration)
Heap Configuration: //堆内存初始化配置
MinHeapFreeRatio = 0 //对应jvm启动参数-XX:MinHeapFreeRatio设置JVM堆最小空闲比率(default 40)
MaxHeapFreeRatio = 100 //对应jvm启动参数 -XX:MaxHeapFreeRatio设置JVM堆最大空闲比率(default 70)
MaxHeapSize = 2082471936 (1986.0MB) //对应jvm启动参数-XX:MaxHeapSize=设置JVM堆的最大大小
NewSize = 1310720 (1.25MB) //对应jvm启动参数-XX:NewSize=设置JVM堆的‘新生代’的默认大小
MaxNewSize = 17592186044415 MB //对应jvm启动参数-XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小
OldSize = 5439488 (5.1875MB) //对应jvm启动参数-XX:OldSize=<value>:设置JVM堆的‘老生代’的大小
NewRatio = 2 //对应jvm启动参数-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
SurvivorRatio = 8 //对应jvm启动参数-XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值
PermSize = 21757952 (20.75MB) //对应jvm启动参数-XX:PermSize=<value>:设置JVM堆的‘永生代’的初始大小
MaxPermSize = 85983232 (82.0MB) //对应jvm启动参数-XX:MaxPermSize=<value>:设置JVM堆的‘永生代’的最大大小
G1HeapRegionSize = 0 (0.0MB)
# 堆使用(Heap Usage)(新生代、老年代)
Heap Usage: //堆内存使用情况
PS Young Generation
Eden Space: //Eden区内存分布
capacity = 33030144 (31.5MB) //Eden区总容量
used = 1524040 (1.4534378051757812MB) //Eden区已使用
free = 31506104 (30.04656219482422MB) //Eden区剩余容量
4.614088270399305% used //Eden区使用比率
From Space: //其中一个Survivor区的内存分布
capacity = 5242880 (5.0MB)
used = 0 (0.0MB)
free = 5242880 (5.0MB)
0.0% used
To Space: //另一个Survivor区的内存分布
capacity = 5242880 (5.0MB)
used = 0 (0.0MB)
free = 5242880 (5.0MB)
0.0% used
PS Old Generation //当前的Old区内存分布
capacity = 86507520 (82.5MB)
used = 0 (0.0MB)
free = 86507520 (82.5MB)
0.0% used
PS Perm Generation //当前的 “永生代” 内存分布
capacity = 22020096 (21.0MB)
used = 2496528 (2.3808746337890625MB)
free = 19523568 (18.619125366210938MB)
11.337498256138392% used
4.2 查看JVM堆中对象详细占用情况
jmap -histo:live <pid> | more
4.3 生成DUMP日志文件分析
jmap -dump:live,format=b,file=dump.dump 20455
dump堆到文件,format指定输出格式,live指明是活着的对象,file指定文件名
jmap -dump:format=b,file=<outputfile.hprof> <pid> #生成hprof文件
4.4 使用jvisualvm装入分析
4.5 其他查看命令
ps命令
ps -p 进程ID -o vsz,rss
VSZ RSS
3701784 413924
VSZ是指已分配的线性空间大小,这个大小通常并不等于程序实际用到的内存大小,产生这个的可能性很多,比如内存映射,共享的动态库,或者向系统申请了更多的堆,都会扩展线性空间大小。
RSZ是Resident Set Size,常驻内存大小,即进程实际占用的物理内存大小
pmap -x 进程ID
Address Kbytes RSS Dirty Mode Mapping
0000000000400000 4 4 0 r-x-- java
0000000000600000 4 4 4 rw--- java
00000000017f8000 2256 2136 2136 rw--- [ anon ]
00000000c4000000 82944 63488 63488 rw--- [ anon ]
00000000c9100000 572416 0 0 ----- [ anon ]
00000000ec000000 27648 27136 27136 rw--- [ anon ]
00000000edb00000 300032 0 0 ----- [ anon ]
......
total kB 3701784 413924 400716
Address: 内存分配地址
Kbytes: 实际分配的内存大小
RSS: 程序实际占用的内存大小
Mapping: 分配该内存的模块的名称
anon(分配内存),这些表示这块内存是由mmap分配的
5.JVM内存
jvn内存划分区,总体上可以分为堆和非堆(基于java8)
java进程最大占用的物理内存为:
Max Memory = eden + survivor + old + String Constant Pool + Code cache + compressed class space + Metaspace + Thread stack(*thread num) + Direct + Mapped + JVM + Native Memory
- 堆和非堆内存
堆和非堆内存有以下几个概念:
- init
表示JVM在启动时从操作系统申请内存管理的初始内存大小(以字节为单位)。JVM可能从操作系统请求额外的内存,也可以随着时间的推移向操作系统释放内存(经实际测试,这个内存并没有过主动释放)。这个init的值可能不会定义。 - used
表示当前使用的内存量(以字节为单位) - committed
表示保证可供 Jvm使用的内存大小(以字节为单位)。 已提交内存的大小可能随时间而变化(增加或减少)。 JVM也可能向系统释放内存,导致已提交的内存可能小于 init,但是committed永远会大于等于used。 - max
表示可用于内存管理的最大内存(以字节为单位)。 - NMT追踪内存
NMT(Native Memory tracking)是一种Java HotSpot VM功能,可跟踪Java HotSpot VM的内部内存使用情况(jdk8+)。
- 执行测试
- 开启
NMT必须先通过VM启动参数中打开,不过要注意的是,打开NMT会增加3MB左右内存,5%-10%CPU的性能损耗。
在启动参数中添加-XX:NativeMemoryTracking=detail
-XX:NativeMemoryTracking=[off | summary | detail]
# off: 默认关闭
# summary: 只统计各个分类的内存使用情况.
# detail: 收集各个分类的内存使用情况
例如:-XX:NativeMemoryTracking=detail
java -XX:NativeMemoryTracking=summary -jar -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m -Xms4096m -Xmx4096m -Xmn512m -Xss512k -XX:SurvivorRatio=8 -XX:+UseG1GC xxx.jar
- 查看
jcmd <pid> VM.native_memory summary scale=MB
- 输出结果
Native Memory Tracking:
Total: reserved=6988749KB, committed=3692013KB
堆内存
- Java Heap (reserved=5242880KB, committed=3205008KB)
(mmap: reserved=5242880KB, committed=3205008KB)
类加载信息
- Class (reserved=1114618KB, committed=74642KB)
(classes #10657)
(malloc=4602KB #32974)
(mmap: reserved=1110016KB, committed=70040KB)
线程栈
- Thread (reserved=255213KB, committed=255213KB)
(thread #248)
(stack: reserved=253916KB, committed=253916KB)
(malloc=816KB #1242)
(arena=481KB #494)
代码缓存
- Code (reserved=257475KB, committed=46551KB)
(malloc=7875KB #10417)
(mmap: reserved=249600KB, committed=38676KB)
垃圾回收
- GC (reserved=31524KB, committed=23560KB)
(malloc=17180KB #2113)
(mmap: reserved=14344KB, committed=6380KB)
编译器
- Compiler (reserved=598KB, committed=598KB)
(malloc=467KB #1305)
(arena=131KB #3)
内部
- Internal (reserved=6142KB, committed=6142KB)
(malloc=6110KB #23691)
(mmap: reserved=32KB, committed=32KB)
符号
- Symbol (reserved=11269KB, committed=11269KB)
(malloc=8544KB #89873)
(arena=2725KB #1)
nmt
- Native Memory Tracking (reserved=2781KB, committed=2781KB)
(malloc=199KB #3036)
(tracking overhead=2582KB)
- Arena Chunk (reserved=194KB, committed=194KB)
(malloc=194KB)
- Unknown (reserved=66056KB, committed=66056KB)
(mmap: reserved=66056KB, committed=66056KB)
reserved
reserved memory 是指JVM 通过mmaped PROT_NONE 申请的虚拟地址空间,在页表中已经存在了记录(entries),保证了其他进程不会被占用。
在堆内存下,就是xmx值,jvm申请的最大保留内存。
committed
committed memory 是JVM向操做系统实际分配的内存(malloc/mmap),mmaped PROT_READ | PROT_WRITE,相当于程序实际申请的可用内存。
注意,committed申请的内存并不是说直接占用了物理内存,由于操作系统的内存管理是惰性的,对于已申请的内存虽然会分配地址空间,但并不会直接占用物理内存,真正使用的时候才会映射到实际的物理内存。所以committed > res也是很可能的
Linux内存与JVM内存
再来说说JVM内存与该进程的内存。
现在有一个Java进程,JVM所有已使用内存区域加起来才2G(不包括Native Memory,也没有显式调用JNI的地方),但从top/pmap上看该进程res已经2.9G了
#top
top -p 7
top - 17:39:40 up 140 days, 5:39, 5 users, load average: 0.00, 0.01, 0.00
Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie
Cpu(s): 0.2%us, 0.1%sy, 0.0%ni, 99.7%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 8059152k total, 5255384k used, 2803768k free, 148872k buffers
Swap: 0k total, 0k used, 0k free, 1151812k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
6267 root 20 0 8930m 2.9g 17m S 0.0 37.6 4:13.31 java
那么其余的0.9G内存去哪了呢?
这时候就要介绍下JVM与Linux内存的联系了
当Java程序启动后,会根据Xmx为堆预申请一块保留内存,并不会直接使用,也不会占用物理内存
然后申请(malloc之类的方法)Xms大小的虚拟内存,但是由于操作系统的内存管理是惰性的,有一个内存延迟分配的概念。malloc虽然会分配内存地址空间,但是并没有映射到实际的物理内存,只有当对该地址空间赋值时,才会真正的占用物理内存,才会影响RES的大小。
所以可能会出现进程所用内存大于当前堆+非堆的情况。
比如说该Java程序在5分钟前,有一定活动,占用了2.6G堆内存(无论堆中的什么代),经过GC之后,虽然堆内存已经被回收了,堆占用很低,但GC的回收只是针对Jvm申请的这块内存区域,并不会调用操作系统释放内存。所以该进程的内存并不会释放,这时就会出现进程内存远远大于堆+非堆的情况。
至于Oracle文档上说的,Jvm可能会向操作系统释放内存,经过测试没有发现释放的情况。不过就算有主动释放的情况,也不太需要我们程序关心了。
java中进程的含义 java进程占用的内存
RES(Resident Set Size)是常驻内存的意思,进程实际使用的物理内存
6.结论
因此如果正常情况下jmap输出的内存占用远小于 RSZ,可以不用太担心,除非发生一些严重错误,比如PermGen空间满了导致OutOfMemoryError发生,或者RSZ太高导致引起系统公愤被OOM Killer给干掉,就得注意了,该加内存加内存,没钱买内存加交换空间,或者按上面列的组成部分逐一排除。
这几个内存指标之间的关系是:VSZ >> RSZ >> Java程序实际使用的堆大小