一、jmap
此命令可以用来查看内存信息。
先用jps命令获得进程ID
jps -lv
23041 blog.jar
2375 sun.tools.jps.Jps -...
1、查看实例个数以及占用内存大小
jmap -histo 23041 > ./log.txt
打开log.txt
num #instances #bytes class name
----------------------------------------------
1: 37816 136896568 [B
2: 482904 97163816 [C
3: 236991 9479640 java.util.TreeMap$Entry
4: 345843 8300232 java.lang.String
5: 18467 3700312 [I
6: 23865 2100120 java.lang.reflect.Method
7: 31700 2028800 java.net.URL
8: 82756 1986144 java.lang.Long
9: 60580 1938560 org.springframework.boot.loader.jar.StringSequence
10: 60044 1921408 java.util.concurrent.ConcurrentHashMap$Node
11: 28726 1505032 [Ljava.lang.Object;
12: 12081 1346744 java.lang.Class
13: 14913 991640 [Ljava.util.HashMap$N
num:序号
instances:实例数量
bytes:占用空间大小
class name:类名称,[C is a char[],[S is a short[],[I is aint[],[B is a byte[],[[I is a int[][]
2、查看堆信息
jmap -heap 23041
Attaching to process ID 23041, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.181-b13
using thread-local object allocation.
Mark Sweep Compact GC
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 482344960 (460.0MB)
NewSize = 10485760 (10.0MB)
MaxNewSize = 160759808 (153.3125MB)
OldSize = 20971520 (20.0MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
New Generation (Eden + 1 Survivor Space):
capacity = 112656384 (107.4375MB)
used = 41996176 (40.05067443847656MB)
free = 70660208 (67.38682556152344MB)
37.278114660594824% used
Eden Space:
capacity = 100204544 (95.5625MB)
used = 41341648 (39.42646789550781MB)
free = 58862896 (56.13603210449219MB)
41.257258752656966% used
From Space:
capacity = 12451840 (11.875MB)
used = 654528 (0.62420654296875MB)
free = 11797312 (11.25079345703125MB)
5.256476151315789% used
To Space:
capacity = 12451840 (11.875MB)
used = 0 (0.0MB)
free = 12451840 (11.875MB)
0.0% used
tenured generation:
capacity = 250109952 (238.5234375MB)
used = 186887384 (178.22969818115234MB)
free = 63222568 (60.293739318847656MB)
74.72209022694148% used
32094 interned Strings occupying 3814360 bytes.
3、堆内存dump
jmap -dump:format=b,file=blog.hprof 23041
然后用jvisualvm工具装入打开来查看。
有时候我们当内存很大的时候可能导不出来,此时我们可以设置内存溢出自动导出dump文件。
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./路径
二、jstack
1、用jstack查找死锁
jstack 线程ID
如图可以看到:
Thread-1
waiting to lock <0x000000076b6ef868>
locked <0x000000076b6ef878>
线程1持有锁<0x000000076b6ef878>,等待锁<0x000000076b6ef868>
Thread-1
waiting to lock <0x000000076b6ef878>
locked <0x000000076b6ef868>
线程2持有锁<0x000000076b6ef868>,等待锁<0x000000076b6ef878>
刚好互相持有互相等待所以造成了死锁。
还可以用jvisualvm自动检测死锁
2、jstack找出占用cpu最高的堆栈信息
1,使用命令top -p
,显示你的java进程的内存情况,pid是你的java进程号,比如4977
2,按H,获取每个线程的内存情况
3,找到内存和cpu占用最高的线程tid,比如4977
4,转为十六进制得到 0x1371 ,此为线程id的十六进制表示
5,执行 jstack 4977|grep -A 10 1371,得到线程堆栈信息中1371这个线程所在行的后面10行
6,查看对应的堆栈信息找出可能存在问题的代码
Unable to open socket file: target process not responding or HotSpot VM not loaded
网上说的解决办法:https://blog.csdn.net/u011250186/article/details/99676292
三、远程连接jvisualvm
1、查看实例个数以及占用内存大小
java -Dcom.sun.management.jmxremote.port=8899 -Dcom.sun.management.jmxremote.ssl=false -
Dcom.sun.management.jmxremote.authenticate=false -jar foo.jar
2、tomcat的JMX配置
JAVA_OPTS=-Dcom.sun.management.jmxremote.port=8899 -Dcom.sun.management.jmxremote.ssl=false -
Dcom.sun.management.jmxremote.authenticate=false
jvisualvm远程连接服务需要在远程服务器上配置host(连接ip 主机名),并且要关闭防火墙
正常不会这样子操作,非常占用资源。
四、jinfo
查看正在运行的Java应用程序的扩展参数
1、查看jvm的参数
jinfo -flags 23041
Attaching to process ID 23041, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.181-b13
Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=31457280 -XX:MaxHeapSize=482344960 -XX:MaxNewSize=160759808 -XX:MinHeapDeltaBytes=196608 -XX:NewSize=10485760 -XX:OldSize=20971520 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops
Command line:
2、查看java系统参数
jinfo -sysprops 23041
Attaching to process ID 23041, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.181-b13
java.runtime.name = Java(TM) SE Runtime Environment
java.vm.version = 25.181-b13
sun.boot.library.path = /usr/local/jdk1.8.0_181/jre/lib/amd64
java.protocol.handler.pkgs = org.springframework.boot.loader
java.vendor.url = http://java.oracle.com/
java.vm.vendor = Oracle Corporation
path.separator = :
file.encoding.pkg = sun.io
java.vm.name = Java HotSpot(TM) 64-Bit Server VM
sun.os.patch.level = unknown
sun.java.launcher = SUN_STANDARD
user.country = US
user.dir = /usr/local/project/blog
java.vm.specification.name = Java Virtual Machine Specification
PID = 23041
java.runtime.version = 1.8.0_181-b13
java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment
os.arch = amd64
java.endorsed.dirs = /usr/local/jdk1.8.0_181/jre/lib/endorsed
line.separator =
java.io.tmpdir = /tmp
java.vm.specification.vendor = Oracle Corporation
os.name = Linux
sun.jnu.encoding = UTF-8
java.library.path = /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
spring.beaninfo.ignore = true
java.specification.name = Java Platform API Specification
java.class.version = 52.0
sun.management.compiler = HotSpot 64-Bit Tiered Compilers
os.version = 3.10.0-957.5.1.el7.x86_64
user.home = /root
user.timezone = Asia/Shanghai
catalina.useNaming = false
java.awt.printerjob = sun.print.PSPrinterJob
file.encoding = UTF-8
java.specification.version = 1.8
catalina.home = /tmp/tomcat.5457169550512388857.8081
user.name = root
java.class.path = blog.jar
java.vm.specification.version = 1.8
sun.arch.data.model = 64
sun.java.command = blog.jar --server.port=8081 --spring.config.location=application-blog.yml
java.home = /usr/local/jdk1.8.0_181/jre
user.language = en
java.specification.vendor = Oracle Corporation
awt.toolkit = sun.awt.X11.XToolkit
java.vm.info = mixed mode
java.version = 1.8.0_181
java.ext.dirs = /usr/local/jdk1.8.0_181/jre/lib/ext:/usr/java/packages/lib/ext
sun.boot.class.path = /usr/local/jdk1.8.0_181/jre/lib/resources.jar:/usr/local/jdk1.8.0_181/jre/lib/rt.jar:/usr/local/jdk1.8.0_181/jre/lib/sunrsasign.jar:/usr/local/jdk1.8.0_181/jre/lib/jsse.jar:/usr/local/jdk1.8.0_181/jre/lib/jce.jar:/usr/local/jdk1.8.0_181/jre/lib/charsets.jar:/usr/local/jdk1.8.0_181/jre/lib/jfr.jar:/usr/local/jdk1.8.0_181/jre/classes
java.awt.headless = true
java.vendor = Oracle Corporation
catalina.base = /tmp/tomcat.5457169550512388857.8081
file.separator = /
java.vendor.url.bug = http://bugreport.sun.com/bugreport/
sun.io.unicode.encoding = UnicodeLittle
sun.font.fontmanager = sun.awt.X11FontManager
sun.cpu.endian = little
sun.cpu.isalist =
五、jstat
jstat命令可以查看堆内存各部分的使用量,以及加载类的数量。命令的格式如下:
jstat [-命令选项] [vmid] [间隔时间(毫秒)] [查询次数]
注意:使用的jdk版本是jdk8.
1、垃圾回收统计
jstat -gc pid 最常用,可以评估程序内存使用及GC压力整体情况
jstat -gc 23041
S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小(元空间)
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间,单位s
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间,单位s
GCT:垃圾回收消耗总时间,单位s
2、堆内存统计
jstat -gccapacity 23041
NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
S0C:第一个幸存区大小
S1C:第二个幸存区的大小
EC:伊甸园区的大小
OGCMN:老年代最小容量
OGCMX:老年代最大容量
OGC:当前老年代大小
OC:当前老年代大小
MCMN:最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:年轻代gc次数
FGC:老年代GC次数
3、新生代垃圾回收统计
jstat -gcnew 23041
S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
TT:对象在新生代存活的次数
MTT:对象在新生代存活的最大次数
DSS:期望的幸存区大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
4、新生代内存统计
jstat -gcnewcapacity 23041
NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
S0CMX:最大幸存1区大小
S0C:当前幸存1区大小
S1CMX:最大幸存2区大小
S1C:当前幸存2区大小
ECMX:最大伊甸园区大小
EC:当前伊甸园区大小
YGC:年轻代垃圾回收次数
FGC:老年代回收次数
5、老年代垃圾回收统计
jstat -gcold 23041
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
OC:老年代大小
OU:老年代使用大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
6、老年代内存统计
jstat -gcoldcapacity 23041
OGCMN:老年代最小容量
OGCMX:老年代最大容量
OGC:当前老年代大小
OC:老年代大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
7、元数据空间统计
jstat -gcmetacapacity 23041
MCMN:最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
8、各代内存使用比例
jstat -gcutil 23041
S0:幸存1区当前使用比例
S1:幸存2区当前使用比例
E:伊甸园区使用比例
O:老年代使用比例
M:元数据区使用比例
CCS:压缩使用比例
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
六、JVM运行情况预估
用 jstat gc -pid 命令可以计算出如下一些关键数据,有了这些数据就可以采用之前介绍过的优化思路,先给自己的系统设置一些初始性的JVM参数,比如堆内存大小,年轻代大小,Eden和Survivor的比例,老年代的大小,大对象的阈值,大龄对象进入老年代的阈值等。
1、年轻代对象增长的速率
可以执行命令 jstat -gc pid 1000 10 (每隔1秒执行1次命令,共执行10次),通过观察EU(eden区的使用)来估算每秒eden大概新增多少对
象,如果系统负载不高,可以把频率1秒换成1分钟,甚至10分钟来观察整体情况。注意,一般系统可能有高峰期和日常期,所以需要在不
同的时间分别估算不同情况下对象增长速率。
2、Young GC的触发频率和每次耗时
知道年轻代对象增长速率我们就能推根据eden区的大小推算出Young GC大概多久触发一次,Young GC的平均耗时可以通过 YGCT/YGC
公式算出,根据结果我们大概就能知道系统大概多久会因为Young GC的执行而卡顿多久。
3、每次Young GC后有多少对象存活和进入老年代
这个因为之前已经大概知道Young GC的频率,假设是每5分钟一次,那么可以执行命令 jstat -gc pid 300000 10 ,观察每次结果eden,
survivor和老年代使用的变化情况,在每次gc后eden区使用一般会大幅减少,survivor和老年代都有可能增长,这些增长的对象就是每次
Young GC后存活的对象,同时还可以看出每次Young GC后进去老年代大概多少对象,从而可以推算出老年代对象增长速率。
4、Full GC的触发频率和每次耗时
知道了老年代对象的增长速率就可以推算出Full GC的触发频率了,Full GC的每次耗时可以用公式 FGCT/FGC 计算得出。
5、优化思路
其实简单来说就是尽量让每次Young GC后的存活对象小于Survivor区域的50%,都留存在年轻代里。尽量别让对象进入老年代。尽量减少Full GC的频率,避免频繁Full GC对JVM性能的影响。
七、内存泄露到底是怎么回事
再给大家讲一种情况,一般电商架构可能会使用多级缓存架构,就是redis加上JVM级缓存,大多数同学可能为了图方便对于JVM级缓存就简单使用一个hashmap,于是不断往里面放缓存数据,但是很少考虑这个map的容量问题,结果这个缓存map越来越大,一直占用着老年代的很多空间,时间长了就会导致full gc非常频繁,这就是一种内存泄漏,对于一些老旧数据没有及时清理导致一直占用着宝贵的内存
资源,时间长了除了导致full gc,还有可能导致OOM。
这种情况完全可以考虑采用一些成熟的JVM级缓存框架来解决,比如ehcache等自带一些LRU数据淘汰算法的框架来作为JVM级的缓存。