关于JVM的一些基础知识,能够看 JVM Internals,曾经我尝试翻译过,地址:http://xiaobaoqiu.github.io/blog/2014/09/11/internal-jvm/html
这里要说的是,那些参数决定了JVM内存上限,好比经常使用的内存配置以下:java
1 |
-Xms1024m -Xmx1024m -XX:PermSize=256m |
说明堆(Heap)的大小1G,永久代大小256M.node
另外,64位的操做系统(Linux服务器),线程的栈空间大小最大为1M:linux
1 2 3 4 5 6 7 |
[baoqiu.xiao@Xxx ...]$ java -XX:+PrintFlagsFinal -version | grep ThreadStackSize intx CompilerThreadStackSize = 0 {pd product} intx ThreadStackSize = 1024 {pd product} intx VMThreadStackSize = 1024 {pd product} java version "1.7.0_45" Java(TM) SE Runtime Environment (build 1.7.0_45-b18) Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode) |
JVM的占用上限由一下几个因素决定:git
1. 堆内存 2. 持久代 3. 线程栈空间 4. 堆外内存
堆外内存参考: http://www.infoq.com/cn/news/2014/12/external-memory-heap-memorygithub
线程数能够以下获得:tomcat
1 2 |
[baoqiu.xiao@Xxx ~]$ ps -m 1381 | wc -l 176 |
一般而言,咱们只须要计算堆内存,持久代再加上线程栈空间.好比咱们的本地一个小服务:服务器
1 |
Max Memory = Heap(1G) + Perm(256M) + 176 * Thread stack (1M) |
首先得知道top命令中RES数据从哪里来的.再Linux上,万物皆文件,所以RES的数据也是来源于文件.session
下面是一个典型的Tomcat应用的线程下的内容,进程号为1381,简单说描述每一个文件的内容或者做用:app
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
attr 进程的属性 autogroup auxv cgroup clear_refs cmdline 启动进程时执行的命令 coredump_filter cpuset cwd 指向进程当前工做目录的软链 environ 进程执行时使用的环境变量 exe 这个就是起这个进程的执行文件 fd 进程打开的文件描述符,能够知道具体的文件路径 fdinfo io 进程的io统计信息 limits 进程的软限制,硬限制等信息 loginuid maps 进程相关的内存映射信息 mem 代进程持有的内存,不可读 mountinfo mounts mountstats net numa_maps oom_adj 调节oom-killer的参数 oom_score oom-killer打分,当须要是,oom-killer会根据各个进程的分数,kill掉某个进程 oom_score_adj 调节oom-killer的参数 pagemap 进程的虚拟页和物理内存页或者swap区的映射关系 personality root 指向进程根目录的软链 sched schedstat sessionid smaps This file shows memory consumption for each of the process's mappings. stack This file provides a symbolic trace of the function calls in this process's kernel stack stat 进程的状态 statm 进程使用的内存的状态 status 进程状态信息,比stat/statm更具可读性 syscall task 进程包含的线程,子目录名是线程的ID wchan |
参考: http://man7.org/linux/man-pages/man5/proc.5.html
和内存相关的文件主要包括(解析各个文件的时间不一样,可能内容上存在对不上的状况):
1. statm: 2. stat: 3. status 4. maps 5. smaps
1. statm
进程的内存使用,注意单位是page,内容的解析参考2.3节的内容.
2. stat
1381这个进程的stat文件及各个字段的含义(含义参见proc命令手册: man proc –> 搜stat, 见/proc/[pid]/stat):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
1381 pid 进程号 (java) comm 应用程序或命令的名字 S state 任务的状态(RSDZTW中一个字符),R:runnign, S:sleeping in an interruptible wait, D:waiting in uninterruptible disk sleep, Z:zombie, T is traced or stopped, W is paging 1 ppid 父进程ID 1374 pgrp 线程组号 1374 session 该任务所在的会话组ID 0 tty_nr 进程的控制终端设备号 -1 tpgid 进程控制终端的前台任务id 4202496 flags 进程内核标志位 988135 minflt 任务不须要从硬盘拷数据而发生的缺页(minor faults)的次数 191 cminflt 任务的全部的waited-for子进程曾经发生的次缺页的次数累计值 252944 majflt 任务须要从硬盘拷数据而发生的缺页(minor faults)的次数 5 cmajflt 任务的全部的waited-for子进程须要从硬盘拷数据而发生的缺页(minor faults)的次数 229942 utime 任务在用户态(User mode)运行的时间,单位为时钟周期(clock ticks) 102219 stime 任务在内核态(kernel mode)运行的时间,单位为时钟周期(clock ticks) 0 cutime 任务的全部的waited-for子进程曾经在用户态运行的时间累计值,单位为时钟周期(clock ticks) 0 cstime 任务的全部的waited-for子进程曾经在核心态运行的时间累计值,单位为时钟周期(clock ticks) 20 priority 任务的动态优先级 0 nice 任务的静态优先级 179 num_threads 进程的线程数 0 itrealvalue 因为计时间隔致使的下一个 SIGALRM 发送进程的时延,单位jiffies 2107287914 starttime 任务启动的时间,单位为jiffies 3968053248 vsize 该任务的虚拟地址空间大小,单位byte 122542 rss 任务当前驻留物理地址空间的大小,单位page 18446744073709551615 rsslim 任务能驻留物理地址空间的最大值,单位byte 4194304 startcode 任务在虚拟地址空间的代码段的起始地址 4196452 endcode 任务在虚拟地址空间的代码段的结束地址 140734635670848 startstack 任务在虚拟地址空间的栈的结束地址 140734635653408 kstkesp ESP(stack pointer, 栈指针)的当前值 250460799149 kstkeip EIP(instruction pointer, 指令指针)的当前值 0 signal pending信号的位图(bitmap),十进制数显示 0 blocked blocked信号的位图(bitmap),十进制数显示 2 sigignore ignored信号的位图(bitmap),十进制数显示 16800973 sigcatch caught信号的位图(bitmap),十进制数显示 18446744073709551615 wchan 进程等待的channel.是系统调用地址 0 nswap 被swapped的页数(当前不用) 0 cnswap 全部子进程被swapped的页数的和(当前不用) 17 exit_signal 该进程结束时,向父进程所发送的信号 3 processor 进程最后一次执行的CPU号 0 rt_priority 实时调度优先级 0 policy 调度策略 4 delayacct_blkio_ticks IO阻塞延迟汇总,单位时钟周期(clock ticks) 0 guest_time 进程的guest time(指费再运行guest操做系统上的时间),单位时钟周期(clock ticks) 0 cguest_time 子进程guest time |
里面包含咱们熟悉的RSS数据,注意其单位是page.
3. status
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
[baoqiu.xiao@Xxx /proc/1381]$ sudo cat status Name: Java /*当前进程的命令*/ State: S (sleeping) /*当前进程状态: "R (running)", "S (sleeping)", "D (disk sleep)", "T (stopped)", "T (tracing stop)", "Z (zombie)", or "X (dead)"*/ Tgid: 1381 /*线程组号*/ Pid: 1381 /*线程id*/ PPid: 1 /*父进程的pid*/ TracerPid: 0 /*跟踪进程的pid,若是没有就是0*/ Uid: 40001 40001 40001 40001 /*uid euid suid fsuid*/ Gid: 40001 40001 40001 40001 /*gid egid sgid fsgid*/ Utrace: 0 FDSize: 512 /*FDSize是当前分配的文件描述符*/ Groups: 40001 /*这里的groups表示启动这个进程的用户所在的组*/ VmPeak: 3880116 kB /*当前进程运行过程当中占用内存的峰值*/ VmSize: 3875052 kB /*进程如今正在占用的内存*/ VmLck: 0 kB /*进程已经锁住的物理内存的大小.锁住的物理内存不能交换到硬盘*/ VmHWM: 766000 kB /*程序获得分配到物理内存的峰值*/ VmRSS: 490168 kB /*程序如今使用的物理内存*/ VmData: 3718132 kB /*进程数据段的大小*/ VmStk: 88 kB /*进程堆栈段的大小*/ VmExe: 4 kB /*进程代码的大小*/ VmLib: 15720 kB /*进程所使用LIB库的大小*/ VmPTE: 2280 kB /*进程所使用LIB库的大小*/ VmSwap: 303616 kB /*进程占用Swap的大小*/ Threads: 179 /*当前进程组线程数*/ SigQ: 0/62812 /*表示当前待处理信号的个数*/ SigPnd: 0000000000000000 /*屏蔽位,存储了该线程的待处理信号,等同于线程的PENDING信号*/ ShdPnd: 0000000000000000 /*屏蔽位,存储了该线程组的待处理信号.等同于进程组的PENDING信号*/ SigBlk: 0000000000000000 /*放被阻塞的信号,等同于BLOCKED信号*/ SigIgn: 0000000000000002 /*存放被忽略的信号,等同于IGNORED信号*/ SigCgt: 2000000181005ccd /*存放捕获的信号,等同于CAUGHT信号*/ CapInh: 0000000000000000 CapPrm: 0000000000000000 CapEff: 0000000000000000 CapBnd: ffffffffffffffff Cpus_allowed: f Cpus_allowed_list: 0-3 Mems_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001 Mems_allowed_list: 0 voluntary_ctxt_switches: 35 nonvoluntary_ctxt_switches: 9 |
关于其中的TracerPid,在Linux下,咱们可使用strace来跟踪命令的运行.
参考: http://blog.csdn.net/zjl410091917/article/details/8075691
4. maps
maps内容以下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[baoqiu.xiao@l-crm3.des.dev.cn0 ~]$ head -30 maps 00400000-00401000 r-xp 00000000 fc:07 1179664 /home/q/java/jdk1.7.0_45/bin/java 00600000-00601000 rw-p 00000000 fc:07 1179664 /home/q/java/jdk1.7.0_45/bin/java 00b78000-00bed000 rw-p 00000000 00:00 0 [heap] aff80000-100000000 rw-p 00000000 00:00 0 3a4fe00000-3a4fe20000 r-xp 00000000 fc:02 2613 /lib64/ld-2.12.so 3a5001f000-3a50020000 r--p 0001f000 fc:02 2613 /lib64/ld-2.12.so 3a50020000-3a50021000 rw-p 00020000 fc:02 2613 /lib64/ld-2.12.so 3a50021000-3a50022000 rw-p 00000000 00:00 0 3a50200000-3a5038a000 r-xp 00000000 fc:02 25297 /lib64/libc-2.12.so 3a5038a000-3a50589000 ---p 0018a000 fc:02 25297 /lib64/libc-2.12.so 3a50589000-3a5058d000 r--p 00189000 fc:02 25297 /lib64/libc-2.12.so 3a5058d000-3a5058e000 rw-p 0018d000 fc:02 25297 /lib64/libc-2.12.so |
第一列表明内存段的虚拟地址 第二列表明执行权限虚拟内存的权限,r=读,w=写,x=,s=共享,p=私有 第三列表明在进程地址里的偏移量 第四列映射文件的主设备号和次设备号 第五列映像文件的节点号,即inode 第六列是映像文件的路径
参考: http://stackoverflow.com/questions/1401359/understanding-linux-proc-id-maps
5. smaps
smaps表明进程的各个线程的内存消耗
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
[baoqiu.xiao@Xxx /proc/1381]$ sudo cat smaps 在smaps文件中,每一条记录(每块)表示进程虚拟内存空间中一块连续的区域. ... 00b78000-00bed000 rw-p 00000000 00:00 0 [heap] /*和maps中的内容相同*/ Size: 468 kB /*表示该映射区域在虚拟内存空间中的大小*/ Rss: 76 kB /*表示该映射区域当前在物理内存中实际占用了多少空间*/ Pss: 76 kB /*该虚拟内存区域平摊计算后使用的物理内存大小*/ Shared_Clean: 0 kB /*和其余进程共享的未被改写的page的大小*/ Shared_Dirty: 0 kB /*和其余进程共享的被改写的page的大小*/ Private_Clean: 0 kB /*未被改写的私有页面的大小*/ Private_Dirty: 76 kB Referenced: 76 kB Anonymous: 76 kB AnonHugePages: 0 kB Swap: 144 kB /*因为物理内存不足被swap到交换空间的大小*/ KernelPageSize: 4 kB /*操做系统一个页面大小*/ MMUPageSize: 4 kB /*CPU页面大小, MMU表示内存管理单元(Memory Management Unit)*/ ... |
关于其中的Pss,由于进程之间存在内存共享,好比该区域所映射的物理内存部分同时也被另外一个进程映射了,且该部分物理内存的大小为1000KB(Rss会是1000KB),那么该进程分摊其中一半的内存,即Pss=500KB。
内存页引用计数>1,则表示是shared,不然是private.
参考: stackoverflow: what does pss mean in /proc/pid/smaps
Getting information about a process' memory usage from /proc/pid/smaps
另外,当前系统的page大小能够经过getconf命令获取,好比我当前的服务器page大小为4k:
1 2 |
[baoqiu.xiao@Xxx ~]$ getconf PAGESIZE 4096 |
top命令RES数据来源于/proc/PID/statm中的第二个字段.仍是1381这个进程的statm文件内容和对应的top信息以下:
1 2 3 4 5 6 |
[baoqiu.xiao@Xxx /proc/1381]$ sudo cat statm 968763 122480 558 1 0 929555 0 [baoqiu.xiao@Xxx ~]$ top -p 1381 PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1381 tomcat 20 0 3784m 478m 2228 S 2.0 6.1 54:53.36 java |
/proc/[pid]/statm文件的几个字段的具体意义以下(注意单位是page):
1 2 3 4 5 6 7 |
size (1) total program size(same as VmSize in /proc/[pid]/status) resident (2) (resident set size)(same as VmRSS in /proc/[pid]/status),即RSS share (3) shared pages (i.e., backed by a file) text (4) text (code) lib (5) library (unused in Linux 2.6) data (6) data + stack dt (7) dirty pages (unused in Linux 2.6) |
能够验证,1381这个进程的RES大小来源, 和top命令中RES的数字是吻合的:
1 |
122480 * 4096 / (1024 * 1024) = 478.4375 MB |
写到这发现谢步下去了,由于我一开始就写错了.
想知道谁吃了咱们JVM的内存,能够jmap dump下来,用MAT分析一下.
想知道谁吃了服务器的内存,建议看看参考里面的Linux Used内存到底哪里去了这篇文章.
参考: Linux Memory Management Overview