java内存占用过高分析

1. Top命令:当前负载机cpu 占用情况,及详解

第一行top05:34:05up 153days0 usersload average: 0.85,0.78,0.66
任务队列表示当前时间系统运行时间当前登录用户数系统负载,即任务队列的平均长度。 三个数值分别为 1分钟、5分钟、15分钟前到现在的平均值
第二行Tasks:38 totals1 running34 sleeping3 stopped0 zombie
进程进程总数正在运行的进程数睡眠的进程数停止的进程数僵尸进程数
第三行%Cpu(s)7.0 us4.7 sy0.0 ni87.5 id0.0 wa0.8 hi0.0 si
CPU的信息用户空间占用CPU百分比内核空间占用CPU百分比用户进程空间内改变过优先级的进程占用CPU百分比空闲CPU百分比等待输入输出的CPU时间百分比硬中断(Hardware IRQ)占用CPU的百分比软中断(Software Interrupts)占用CPU的百分比
第四行 KiB Mem:32626.0 total2252.2 free12445.7 used17938.1 buff/cache
内存信息物理内存总量使用的物理内存总量空闲内存总量用作内核缓存的内存量
第五行 KiB Swap32767.9 total32439.9.2 free328.0 used15132.6 buff/cache
交换区总量使用的交换区总量空闲交换区总量代表可用于进程下一次分配的物理内存数量
进程信息PIDUSERNIVIRTRESSHRS%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。

S0CS1CS0US1UECEUOCOUMCMUCCSCCCSUYGCYGCTFGCFGCTGCT
第一个幸存区的大小第二个幸存区的大小第一个幸存区的使用大小第二个幸存区的使用大小伊甸园区的大小伊甸园区的使用大小老年代大小老年代使用大小方法区大小方法区使用大小压缩类空间大小压缩类空间使用大小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
  1. 堆和非堆内存
    堆和非堆内存有以下几个概念:
  • init
    表示JVM在启动时从操作系统申请内存管理的初始内存大小(以字节为单位)。JVM可能从操作系统请求额外的内存,也可以随着时间的推移向操作系统释放内存(经实际测试,这个内存并没有过主动释放)。这个init的值可能不会定义。
  • used
    表示当前使用的内存量(以字节为单位)
  • committed
    表示保证可供 Jvm使用的内存大小(以字节为单位)。 已提交内存的大小可能随时间而变化(增加或减少)。 JVM也可能向系统释放内存,导致已提交的内存可能小于 init,但是committed永远会大于等于used。
  • max
    表示可用于内存管理的最大内存(以字节为单位)。
  • NMT追踪内存
    NMT(Native Memory tracking)是一种Java HotSpot VM功能,可跟踪Java HotSpot VM的内部内存使用情况(jdk8+)。
  1. 执行测试
  • 开启
    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程序实际使用的堆大小

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值