作者归档:space

java 内存分析-JAVA进程内存用量高的分析与解决

转自:http://chou.it/2013/09/why-java-consume-a-lot-of-rss-memory

首先看一下一个java进程的jmap输出:

[lex@chou ~]$ jmap -heap 837
Attaching to process ID 837, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 20.10-b01

using thread-local object allocation.
Parallel GC with 2 thread(s)

Heap Configuration:
   MinHeapFreeRatio = 40
   MaxHeapFreeRatio = 70
   MaxHeapSize      = 4294967296 (4096.0MB)
   NewSize          = 1310720 (1.25MB)
   MaxNewSize       = 17592186044415 MB
   OldSize          = 5439488 (5.1875MB)
   NewRatio         = 2
   SurvivorRatio    = 8
   PermSize         = 21757952 (20.75MB)
   MaxPermSize      = 85983232 (82.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 41025536 (39.125MB)
   used     = 18413552 (17.560531616210938MB)
   free     = 22611984 (21.564468383789062MB)
   44.883147900858624% used
From Space:
   capacity = 4325376 (4.125MB)
   used     = 3702784 (3.53125MB)
   free     = 622592 (0.59375MB)
   85.60606060606061% used
To Space:
   capacity = 4521984 (4.3125MB)
   used     = 0 (0.0MB)
   free     = 4521984 (4.3125MB)
   0.0% used
PS Old Generation
   capacity = 539820032 (514.8125MB)
   used     = 108786168 (103.74657440185547MB)
   free     = 431033864 (411.06592559814453MB)
   20.152302906758376% used
PS Perm Generation
   capacity = 85983232 (82.0MB)
   used     = 60770232 (57.95500946044922MB)
   free     = 25213000 (24.04499053955078MB)
   70.67684080542588% used

然后再用ps看看:

[lex@chou ~]$ ps -p 837 -o vsz,rss
   VSZ   RSS
7794992 3047320

关于这里的几个generation网上资料一大把就不细说了,这里算一下求和可以得知前者总共给Java环境分配了644M的内存,而ps输出的VSZ和RSS分别是7.4G和2.9G,这到底是怎么回事呢?
前面jmap输出的内容里,MaxHeapSize 是在命令行上配的,-Xmx4096m,这个java程序可以用到的最大堆内存。
VSZ是指已分配的线性空间大小,这个大小通常并不等于程序实际用到的内存大小,产生这个的可能性很多,比如内存映射,共享的动态库,或者向系统申请了更多的堆,都会扩展线性空间大小,要查看一个进程有哪些内存映射,可以使用 pmap 命令来查看:

[lex@chou ~]$ pmap -x 837
837:   java
Address           Kbytes     RSS   Dirty Mode   Mapping
0000000040000000      36       4       0 r-x--  java
0000000040108000       8       8       8 rwx--  java
00000000418c9000   13676   13676   13676 rwx--    [ anon ]
00000006fae00000   83968   83968   83968 rwx--    [ anon ]
0000000700000000  527168  451636  451636 rwx--    [ anon ]
00000007202d0000  127040       0       0 -----    [ anon ]
...
...
00007f55ee124000       4       4       0 r-xs-  az.png
00007fff017ff000       4       4       0 r-x--    [ anon ]
ffffffffff600000       4       0       0 r-x--    [ anon ]
----------------  ------  ------  ------
total kB         7796020 3037264 3023928

这里可以看到很多anon,这些表示这块内存是由mmap分配的。

RSZ是Resident Set Size,常驻内存大小,即进程实际占用的物理内存大小, 在现在这个例子当中,RSZ和实际堆内存占用差了2.3G,这2.3G的内存组成分别为:

JVM本身需要的内存,包括其加载的第三方库以及这些库分配的内存
NIO的DirectBuffer是分配的native memory
内存映射文件,包括JVM加载的一些JAR和第三方库,以及程序内部用到的。上面 pmap 输出的内容里,有一些静态文件所占用的大小不在Java的heap里,因此作为一个Web服务器,赶紧把静态文件从这个Web服务器中人移开吧,放到nginx或者CDN里去吧。
JIT, JVM会将Class编译成native代码,这些内存也不会少,如果使用了Spring的AOP,CGLIB会生成更多的类,JIT的内存开销也会随之变大,而且Class本身JVM的GC会将其放到Perm Generation里去,很难被回收掉,面对这种情况,应该让JVM使用ConcurrentMarkSweep GC,并启用这个GC的相关参数允许将不使用的class从Perm Generation中移除, 参数配置: -XX:+UseConcMarkSweepGC -X:+CMSPermGenSweepingEnabled -X:+CMSClassUnloadingEnabled,如果不需要移除而Perm Generation空间不够,可以加大一点: -X:PermSize=256M -X:MaxPermSize=512M
JNI,一些JNI接口调用的native库也会分配一些内存,如果遇到JNI库的内存泄露,可以使用valgrind等内存泄露工具来检测
线程栈,每个线程都会有自己的栈空间,如果线程一多,这个的开销就很明显了
jmap/jstack 采样,频繁的采样也会增加内存占用,如果你有服务器健康监控,记得这个频率别太高,否则健康监控变成致病监控了。
关于JVM的几个GC堆和GC的情况,可以用jstat来监控,例如监控进程837每隔1000毫秒刷新一次,输出20次:

[lex@chou ~]$ jstat -gcutil 837 1000 20
  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT   
  0.00  80.43  24.62  87.44  98.29   7101  119.652    40   19.719  139.371
  0.00  80.43  33.14  87.44  98.29   7101  119.652    40   19.719  139.371

几个字段分别含义如下:

S0 年轻代中第一个survivor(幸存区)已使用的占当前容量百分比
S1 年轻代中第二个survivor(幸存区)已使用的占当前容量百分比
E 年轻代中Eden(伊甸园)已使用的占当前容量百分比
O old代已使用的占当前容量百分比
P perm代已使用的占当前容量百分比
YGC 从应用程序启动到采样时年轻代中gc次数
YGCT 从应用程序启动到采样时年轻代中gc所用时间(s)
FGC 从应用程序启动到采样时old代(全gc)gc次数
FGCT 从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT 从应用程序启动到采样时gc用的总时间(s)
结论
因此如果正常情况下jmap输出的内存占用远小于 RSZ,可以不用太担心,除非发生一些严重错误,比如PermGen空间满了导致OutOfMemoryError发生,或者RSZ太高导致引起系统公愤被OOM Killer给干掉,就得注意了,该加内存加内存,没钱买内存加交换空间,或者按上面列的组成部分逐一排除。
这几个内存指标之间的关系是:VSZ >> RSZ >> Java程序实际使用的堆大小

相关的参考资料:

http://www.ibm.com/developerworks/java/library/j-nativememory-linux/

http://www.oracle.com/technetwork/java/javase/index-137495.html

linux下使用私有驱动时需谨慎升级kernel 和 x server

我的系统是 linux mint 13 xfce 版本, 显卡为ATI 的。 曾经为了追求最大限度发挥显卡性能而安装了ATI的私有驱动。

今天,我在配置一个软件过程中,执行了

     sudo apt-get upgrade
   

命令, 命令提醒我可以安装新版本的linux kernel 和 x server了, 我一看,好啊!新版本应该好很多! 然后立马输入了 “Y”, 让系统升级。
升级完成后,我继续使用电脑做别的事情,一切看起来非常的正常、平静。没有任何要发生异常的预兆。
晚上,我想电脑累了一天,重启下吧! 这下意外发生了,“我的系统启动后没有任何提示!黑屏啊!” 我想会不会因为升级导致grub不能引导?还是kernel 有问题了? 我强制关机,继续重启,有时居然可以进入linux mint 的启动界面, 但会卡在这里不动,而且奇怪的是,这个页面和以前不一样,字体明显很
粗糙。

此时我分析可能出错的地方有:
1. grub 引导程序损坏
2. 新kernel 有bug
3. x server 有问题

继续分析:如果grub引导坏掉,不可能有时可以进入linux mint 启动界面,所以可能性不大。kernel 如果有问题,也不可能进入linux mint 启动界面,更不应该在我 按 ctrl + alt + f1 时可以进入 非图形界面的。所以,出问题可能性最大的应该就是x server 了。

接下来,重启电脑,启动时注意按 SHIFT键, 在grub 引导菜单里面选择 recovery model 的菜单, 进入后,选择 “resume Resume normal boot ”, 按tab键将光标移动到 OK 按钮,回车,系统提示说“x server 不能启动,配置有问题。” 这样,我们就可以确认x server 不能正常启动了。

那我们就看看

     nano /etc/X11/xorg.conf
    

发现我的配置是ati的驱动,有下面内容,

Section "Monitor"
	Identifier   "aticonfig-Monitor[0]-0"
	Option	    "VendorName" "ATI Proprietary Driver"
	Option	    "ModelName" "Generic Autodetecting Monitor"
	Option	    "DPMS" "true"
EndSection

Section "Device"
	Identifier  "aticonfig-Device[0]-0"
	Driver      "fglrx"
	BusID       "PCI:1:0:0"
EndSection
    

猜测是系统x server , kernel 升级后私有驱动不兼容导致,那恢复成开源驱动。恢复方法为:卸载私有驱动。

恢复步骤:
1. 重启系统,正常启动过程中(不是recovery model), 按 ctrl + alt + f1 进入命令行。
2. 登录后, 切换为root用户

          sudo -i
        

3. 卸载ati 私有驱动

sudo sh /usr/share/ati/fglrx-uninstall.sh
sudo apt-get remove --purge fglrx fglrx_* fglrx-amdcccle* fglrx-dev*
        

4. 重启系统

           sudo reboot
         

这次启动过程好像比较慢,但是,终于看到熟悉的 linux mint 登录界面了。说明系统恢复了!

这次,我差一点就行重装系统,重装很麻烦,最终曲折的恢复了系统。

不过,从这件事情上面得到歌总结:
如果安装了私有驱动 , 升级系统的 linux kernel 和 x server时要特别小心。最好不要升级这两个项目。

出发有特别需要,电脑上面的系统级软件还是不升级为妙。稳定才是我们的根本需求。