内存溢出
内存溢出的原因:程序在申请内存时,没有足够的内存空间。
内存溢出的几种方式:
1.栈溢出:方法死循环递归调用(StackOverflowError)、不断建立线程(OurOfMemoryError)
2.堆溢出:不断创建对象,分配对象大于最大堆的大小(OurOfMemoryError)
3.直接内存:分配的本地内存大小大于JVM的限制
4.方法区溢出:在经常动态生产大量Class的应用中,CGLib字节码增强,动态语言,大量JSP,基于OSGi的应用
内存泄漏
程序在申请内存后,无法释放已申请的内存空间
内存泄漏的几种方式:
1.长生命周期的对象持有短生命周期对象的引用:例如将ArrayList设置为静态常量,则容器中的对象在程序结束前是不能被释放,造成内存泄漏。
2.连接未关闭 : 数据库连接、网络链接和IO链接等
3.变量作用域不合理 : 1.一个变量定义的作用范围大于其适用范围。2,没有及时把对象设置为null
4.内部类持有外部类 :非静态内部类创建或持有外部类的引用,默认是强引用,因此,内部类的生命周期长于外部类,很容易内存泄漏
5.Hash值改变 : 集合中修改了对象中的那些参与计算哈希值的字段,会导致无法从集合中单独删除当前对象,造成内存泄漏。
内存泄漏和内存溢出辨析
相同与不同:
内存溢出:实实在在的内存空间不足导致
内存泄漏:该释放的对象没有释放,多见于自己使用容器保存原的情况下。
如何避免:
内存溢出:检查代码以及设置足够的空间
内存泄漏:一定代码有问题
往往很多情况下,内存溢出就是内存泄漏造成的。
GC调优原则
- 大多数的java应用不需要GC调优
- 大部分需要GC调优的,不是参数问题,而是代码问题
- 在实际项目中,分析GC情况优化代码比调整GC参数要重要
- GC调优是最后的手段
目的:
- GC的时间足够小
- GC的次数足够小
调优步骤:
- 监视GC的状态
- 分析结果,判断是否需要优化
- 调整GC类型和内存分配
- 不断分析和调整
- 全面应用参数
逃逸分析
栈上分配
-XX:+DoEscapeAnalysis:启用逃逸分析(默认打开)
-XX:+EliminateAllocations:标量替换(默认打开)
-XX:+UseTLAB 本地线程分配缓冲(默认打开)
如果是逃逸分析出来的对象可以在栈上分配,就不用垃圾回收
没有逃逸分析的对象都在堆上分配(触发频繁GC,加重负担)
JVM调优指令
1. jps
jps是jdk提供的一个查看当前java进程的小工具
jps [option] [hostid]
option参数可输也可不输。
-l: 输出主类全名或jar路径
-q: 只输出LVMID
-m: 输出JVM启动时传递给main()的参数
-v: 输出JVM启动时显示指定的JVM参数
2. jstat
jstat的主要作用就是对Java应用程序的资源和性能进行实时监控的命令行工具,主要包括GC情况和Heap Size资源 使用情况。
jstat [option] vmid [interval] [count]
option: 操作参数
vmid: 本地虚拟机进程ID
interval: 连续输出的时间间隔
count: 连续输出的次数
下面是对一些常用的option参数的解释,注意1797是通过jps -l指令执行过程中得到的id,即我本地运行的Tomcat的id
-compiler 编译统计
jstat -compiler 1797
Compiled : 编译数量
Failed : 编译失败数量
Invalid : 无效数量
Time : 编译耗时
FailedType : 失败类型
FailedMethod : 失败方法的全限定名
-class 类加载统计
jstat -class 1797
Loaded : 加载class的数量
Bytes : class字节大小
Unloaded : 未加载class的数量
Bytes : 未加载class的字节大小
Time : 加载时间
-gc 垃圾回收统计
jstat -gc 1797
S0C : survivor0区的总容量
S1C : survivor1区的总容量
S0U : survivor0区已使用的容量
S1U : survivor1区已使用的容量
EC : Eden区的总容量
EU : Eden区已使用的容量
OC : Old区的总容量
OU : Old区已使用的容量
PC : 当前perm的容量 (KB)
PU : perm的使用 (KB)
YGC : 新生代垃圾回收次数
YGCT : 新生代垃圾回收时间
FGC : 老年代垃圾回收次数
FGCT : 老年代垃圾回收时间
GCT : 垃圾回收总消耗时间
jstat -gc 1797 5000
5秒打印一次
-gcutil 总结垃圾回收统计
jstat -gcutil 1797
S0 : survivor0区的总容量
S1 : survivor1区的总容量
E : Eden区的总容量
O : Old区的总容量
OU : Old区已使用的容量
YGC : 新生代垃圾回收次数
YGCT : 新生代垃圾回收时间
FGC : 老年代垃圾回收次数
FGCT : 老年代垃圾回收时间
GCT : 垃圾回收总消耗时间
-gccause gc原因调查
jstat -gccause 1797
LGCC:最近垃圾回收的原因
GCC:当前垃圾回收的原因
-gccapacity 堆内存统计
jstat -gccapacity 1797
NGCMN : 新生代占用的最小空间
NGCMX : 新生代占用的最大空间
OGCMN : 老年代占用的最小空间
OGCMX : 老年代占用的最大空间
OGC:当前年老代的容量 (KB)
OC:当前年老代的空间 (KB)
PGCMN : perm占用的最小空间
PGCMX : perm占用的最大空间
3. jmap 生成虚拟机的内存转储快照(heapdump)文件
jmap [option] vmid
jmap(JVM Memory Map)命令用于生成heap dump文件,如果不使用这个命令,还可以使用-XX:+HeapDumpOnOutOfMemoryError参数来让虚拟机出现OOM时自动生成dump文件。jmap不仅能生成dump文件,还能查询finalize执行队列、Java堆和永久代的详细信息,如当前使用率、当前使用的是哪种收集器等。
dump : 生成堆转储快照
finalizerinfo : 显示在F-Queue队列等待Finalizer线程执行finalizer方法的对象
heap : 显示Java堆详细信息
histo : 显示堆中对象的统计信息
permstat : to print permanent generation statistics
F : 当-dump没有响应时,强制生成dump快照
Attaching to process ID 94358, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.79-b02
using thread-local object allocation.
Parallel GC with 8 thread(s)
Heap Configuration: //堆内存初始化配置
MinHeapFreeRatio = 0 //jvm参数-XX:MinHeapFreeRatio设置JVM堆最小空闲比率(default 40)
MaxHeapFreeRatio = 100 //jvm参数 -XX:MaxHeapFreeRatio设置JVM堆最大空闲比率(default 70)
MaxHeapSize = 1073741824 (1024.0MB) //jvm参数-XX:MaxHeapSize=设置JVM堆的最大大小
NewSize = 536870912 (512.0MB)//jvm参数-XX:NewSize=设置JVM堆的‘新生代’的默认大小
MaxNewSize = 536870912 (512.0MB)//jvm参数-XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小
OldSize = 5439488 (5.1875MB) //参数-XX:OldSize=<value>:设置JVM堆的‘老生代’的大小
NewRatio = 2 //参数-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
SurvivorRatio = 8 //参数-XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值
PermSize = 536870912 (512.0MB) //参数-XX:PermSize=<value>:设置JVM堆的‘永生代’的初始大小
MaxPermSize = 1073741824 (1024.0MB)//参数-XX:MaxPermSize=<value>:设置JVM堆的‘永生代’的最大大小
G1HeapRegionSize = 0 (0.0MB)
Heap Usage: //堆内存使用情况
PS Young Generation
Eden Space:
capacity = 300417024 (286.5MB) //总容量
used = 231151904 (220.44363403320312MB) //已使用
free = 69265120 (66.05636596679688MB)//剩余容量
76.94367680041994% used //Eden区使用比率
From Space: // S0 内存使用情况
capacity = 120586240 (115.0MB)
used = 53528904 (51.04914093017578MB)
free = 67057336 (63.95085906982422MB)
44.39055733058763% used
To Space: // S1 内存使用情况
capacity = 115867648 (110.5MB)
used = 0 (0.0MB)
free = 115867648 (110.5MB)
0.0% used
PS Old Generation // Old区使用情况
capacity = 536870912 (512.0MB)
used = 241598968 (230.40673065185547MB)
free = 295271944 (281.59326934814453MB)
45.00131458044052% used
PS Perm Generation // 永久代使用情况
capacity = 536870912 (512.0MB)
used = 143245904 (136.6099395751953MB)
free = 393625008 (375.3900604248047MB)
26.681628823280334% used
18942 interned Strings occupying 2284376 bytes.
也可以使用dump参数,将dump文件保存到本地,然后再通过工具来分析。
-dump::live,format=b,file=<filename> pid
$ ./jmap -dump:live,format=b,file=/tmp/myDump.hprof 94358
Dumping heap to /private/tmp/myDump.hprof ...
Heap dump file created
4. jstack 生成java虚拟机当前时刻的线程快照
jstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。
[root@localhost bin]# jstack -help
Usage:
jstack [-l] <pid>
(to connect to running process)
jstack -F [-m] [-l] <pid>
(to connect to a hung process)
jstack [-m] [-l] <executable> <core>
(to connect to a core file)
jstack [-m] [-l] [server_id@]<remote server IP or hostname>
(to connect to a remote debug server)
Options:
-F to force a thread dump. Use when jstack <pid> does not respond (process is hung)
-m to print both java and native frames (mixed mode)
-l long listing. Prints additional information about locks
-h or -help to print this help message
-F:当正常输出请求不被响应时,强制输出线程栈堆。
-l:除线程栈堆外,显示关于锁的附加信息。
-m:如果调用本地方法的话,可以显示c/c++的栈堆
[root@localhost bin]# jstack -m 1797 | more
Attaching to process ID 24971, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.79-b02
Deadlock Detection:
No deadlocks found.
----------------- 24972 -----------------
0x00007fe07d2cd69d __GI___poll + 0x2d
0x00007fe070dc80a7 Java_java_net_PlainSocketImpl_socketAccept + 0x1e7
0x00007fe0745a3d98 * java.net.PlainSocketImpl.socketAccept(java.net.SocketImpl) bci:0 (Interpreted frame)
0x00007fe074597058 * java.net.AbstractPlainSocketImpl.accept(java.net.SocketImpl) bci:7 line:398 (Interpreted frame)
0x00007fe074597058 * java.net.ServerSocket.implAccept(java.net.Socket) bci:60 line:530 (Interpreted frame)
0x00007fe074597058 * java.net.ServerSocket.accept() bci:48 line:498 (Interpreted frame)
0x00007fe074597233 * org.apache.catalina.core.StandardServer.await() bci:180 line:470 (Interpreted frame)
0x00007fe074597706 * org.apache.catalina.startup.Catalina.await() bci:4 line:782 (Interpreted frame)
0x00007fe074597058 * org.apache.catalina.startup.Catalina.start() bci:209 line:728 (Interpreted frame)
0x00007fe0745914e7 <StubRoutines>
0x00007fe07c967e95 _ZN9JavaCalls11call_helperEP9JavaValueP12methodHandleP17JavaCallArgumentsP6Thread + 0x365
0x00007fe07c9668f8 _ZN9JavaCalls4callEP9JavaValue12methodHandleP17JavaCallArgumentsP6Thread + 0x28
0x00007fe07cbfdeef _ZN10Reflection6invokeE19instanceKlassHandle12methodHandle6Handleb14objArrayHandle9BasicTypeS3_bP6Thread + 0x
47f
0x00007fe07cbfeca0 _ZN10Reflection13invoke_methodEP7oopDesc6Handle14objArrayHandleP6Thread + 0x160