Who Eats JVM's Memory

1.JVM內存佔用上限

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

2.JVM RES數據來源

首先得知道top命令中RES數據從哪裏來的.再Linux上,萬物皆文件,所以RES的數據也是來源於文件.session

2.1 /proc/pid內容

下面是一個典型的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

2.2 /proc/pid下內存相關文件

和內存相關的文件主要包括(解析各個文件的時間不一樣,可能內容上存在對不上的狀況):

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

2.3 top命令RES數據來源

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

3.誰吃了JVM內存

寫到這發現謝步下去了,由於我一開始就寫錯了.

想知道誰吃了咱們JVM的內存,能夠jmap dump下來,用MAT分析一下.

想知道誰吃了服務器的內存,建議看看參考裏面的Linux Used內存到底哪裏去了這篇文章.

參考: Linux Memory Management Overview

Understanding Process memory

How much memory are applications really using

Memory Management

Linux Used內存到底哪裏去了

也看linux內存去哪兒了

相關文章
相關標籤/搜索