背景
线上环境的java进程,内存占用持续增高,没有下降的迹象。到网上看了几篇文章,结合以前看的书,对JVM参数进行了调整。
运行环境:linux + jetty
过程如下
1. 使用'top'命令查看java进程的'pid'(同时也能看到cup和内存使用率)
2. jmap -heap pid
使用该命令查看JVM的内存分配情况,举个例子(不是真实环境):
[root@localhost ~]# jmap -heap 21265
Attaching to process ID 21265, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.51-b03
using thread-local object allocation.
Parallel GC with 4 thread(s) --当前使用的GC收集器
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 2025848832 (1932.0MB)
NewSize = 1310720 (1.25MB)
MaxNewSize = 17592186044415 MB
OldSize = 5439488 (5.1875MB)
NewRatio = 2
SurvivorRatio = 8
PermSize = 21757952 (20.75MB)
MaxPermSize = 85983232 (82.0MB)
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
PS Young Generation<span style="white-space:pre"> </span>-- 新生代
</span>Eden Space:
capacity = 116391936 (111.0MB)
used = 105991496 (101.08136749267578MB)
free = 10400440 (9.918632507324219MB)
91.06429503844664% used
From Space:
capacity = 12582912 (12.0MB)
used = 12552608 (11.971099853515625MB)
free = 30304 (0.028900146484375MB)
99.75916544596355% used
To Space:
capacity = 3145728 (3.0MB)
used = 0 (0.0MB)
free = 3145728 (3.0MB)
0.0% used
PS Old Generation<span style="white-space:pre"> </span>--老年代
capacity = 84410368 (80.5MB)
used = 23076840 (22.007789611816406MB)
free = 61333528 (58.492210388183594MB)
27.338869083001747% used
PS Perm Generation
capacity = 35127296 (33.5MB)
used = 34650048 (33.04486083984375MB)
free = 477248 (0.45513916015625MB)
98.64137564132463% used
15850 interned Strings occupying 1578368 bytes.
分析一下这个配置存在的问题:
①MaxNewSize过高。NewSize和MaxNewSize设置得不同,会导致JVM动态调整新生代内存,产生额外的开销。
②From Space和To Space设置太小。新生代的内存回收算法使用'复制算法',就是利用这两个空间。设置太小会导致内存较大的对象晋升到老年代,无法及时回收
3. 针对这两个问题调整的参数
1)-XX:NewSize和-XX:MaxNewSize
用于设置年轻代的大小,建议设为整个堆大小的1/3或者1/4,两个值设为一样大。
2)-XX:SurvivorRatio
用于设置Eden和其中一个Survivor的比值,这个值也比较重要。比如4,那么eden:from:to=4:1:1,假如新生代内存为1024,那么eden区占682M,from和to占170M。
另外,
①使用jmap生成dump文件
[root@localhost ~]# jmap -dump:file=dump0812 21265
Dumping heap to /root/dump0812 ...
Heap dump file created
②使用jmap查看java类在内存中的使用情况
[root@localhost ~]# jmap -histo 21265 > 21265.log
[root@localhost ~]# vim 21265.log
num #instances #bytes class name
----------------------------------------------
1: 628 41166656 [Lorg.apache.activemq.command.DataStructure;
2: 181495 17662128 [C
3: 83874 13812408 [B
4: 21310 11787984 [I
5: 62129 9034024 <constMethodKlass>
6: 62129 7964432 <methodKlass>
7: 5690 6305352 <constantPoolKlass>
8: 52726 4218080 java.lang.reflect.Method
9: 5689 4112944 <instanceKlassKlass>
10: 4972 3777824 <constantPoolCacheKlass>
11: 100670 2416080 java.lang.String
12: 43910 2234608 [Ljava.lang.Object;
13: 36393 1164576 java.util.HashMap$Entry
14: 1780 966400 <methodDataKlass>
15: 13277 946248 [S
16: 19086 763440 java.util.concurrent.ConcurrentHashMap$ValueIterator
17: 30792 739008 java.util.IdentityHashMap$EntryIterator$Entry
18: 6050 727096 java.lang.Class
19: 7998 575856 java.lang.reflect.Field
20: 26766 557504 [Ljava.lang.Class;
21: 11266 540768 java.util.HashMap
22: 21420 514080 java.lang.StringBuilder
23: 4604 502304 [Ljava.util.HashMap$Entry;
24: 8911 461960 [[I
25: 13733 329592 java.util.ArrayList
26: 7900 316000 java.lang.ref.SoftReference
27: 1470 249152 [Ljava.lang.reflect.Method;
28: 7595 243040 java.util.concurrent.locks.ReentrantLock$NonfairSync
29: 7591 242912 java.lang.ref.WeakReference
30: 3182 229104 java.lang.reflect.Constructor
31: 5864 205512 [Ljava.lang.String;
③这里使用的是parallel收集器,此时设置JVM参数SurvivorRatio无效,网友的回答是这样的:
HotSpot VM里,ParallelScavenge系的GC(UseParallelGC / UseParallelOldGC)默认行为是SurvivorRatio如果不显式设置就没啥用。显式设置到跟默认值一样的值则会有效果。因为ParallelScavenge系的GC最初设计就是默认打开AdaptiveSizePolicy的,它会自动、自适应的调整各种参数
为了让这个参数有效,又修改了JVM使用的收集器为CMS:-XX:+UseConcMarkSweepGC
最终
-XX:NewSize=1024
-XX:MaxNewSize=1024
-XX:SurvivorRatio=4
-XX:+UseConcMarkSweepGC