關於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