OOM如何解决?
最常见的原因为:
-
有可能是内存分配确实过小,而正常业务使用了大量内存
-
某一个对象被频繁申请,却没有释放,内存不断泄漏,导致内存耗尽
-
某一个资源被频繁申请,系统资源耗尽,例如:不断创建线程,不断发起网络连接
通过jprofer观察所有对象,触发gc后观察变化。得到不断递增的对象找到问题点。
https://my.oschina.net/u/3705388/blog/1611985
通过命令排查
确认是不是内存本身就分配过小
方法:jmap -heap pid
可以查看新生代,老生代堆内存的分配大小以及使用情况,看是否本身分配过小。
找到最耗内存的对象
方法:jmap -histo:live pid | more
输入命令后,会以表格的形式显示存活对象的信息,并按照所占内存大小排序
确认是否是资源耗尽
工具:
-
pstree
-
netstat
查看进程创建的线程数,以及网络连接数,如果资源耗尽,也可能出现OOM。
这里介绍另一种方法,通过
-
/proc/${PID}/fd
-
/proc/${PID}/task
可以分别查看句柄详情和线程数。
例如,某一台线上服务器的sshd进程PID是9339,查看
-
ll /proc/9339/fd
-
ll /proc/9339/task
https://mp.weixin.qq.com/s/tVvqVVigmvzLfPjnt2oK0g
通过对dump文件分析,jvisualvm查看对象,分析占内存最多的对象,查看对象内容,找到对象引用找到问题点。
http://blog.csdn.net/wilsonpeng3/article/details/70064336
栈溢出完全解读 (StackOverflowError)
导致栈溢出的原因很多,提三个主要的:
- java代码写得不当,比如出现递归死循环,这也是最常见的,只能靠写代码的人小心了
- native代码有栈上分配的逻辑,并且要求的内存还不小
- 线程栈空间设置比较小
https://mp.weixin.qq.com/s/1d8W-eyzsnGDr9-uSfag6Q
解决CPU占用过高?
根据top命令,发现cpu占用高的进程PID,或者通过ps aux | grep PID命令查看。
定位到具体线程: ps -mp pid -o THREAD,tid,time 或者 ps -Lfp pid 或者 top -Hp pid,看到具体线程占用cpu比例和时间
将tid转成16进制数字:printf "%x\n" tid
最后打印出线程栈信息:jstack pid |grep tid -A 30
https://mp.weixin.qq.com/s/fU4i-jDVHgdJfRtWtXaxrQ
https://mp.weixin.qq.com/s/c-KuGjI_VH1dTxIWtxZJEg
https://www.cnblogs.com/ylz8401/p/6170775.html
http://blog.csdn.net/zhouree/article/details/45153417
http://blog.csdn.net/zhanglh046/article/details/50443528
http://www.xitongzhijia.net/xtjc/20141203/31828.html
死锁如何解决?
java查看线程死锁 JConsole,选择监控进程后,选择 查看死锁线程,可以列出死锁/阻塞线程
https://my.oschina.net/u/3705388/blog/1596538
本质是 jstack 获取线程栈,
找出Thread.State:BLOCKED的线程,对比线程waiting to lock<xxx> 和locked<yyy>,
有没有两个线程的xxx和yyy是相反的,即相互等待对方持有的锁。
IO高如何定位
Step1 : iostat 查看IO情况
Step2: iotop定位负载来源进程
iotop的本质是一个python脚本,从proc中获取thread的IO信息,进行汇总。
Step3 pt-ioprofile定位负载来源文件
pt-ioprofile的原理是对某个pid附加一个strace进程进行IO分析。
https://www.cnblogs.com/zengkefu/p/5634306.html
https://blog.csdn.net/aquester/article/details/51557879
线上动态增加接口调用情况方法:
BTrace是Java的安全可靠的动态跟踪工具。 他的工作原理是通过 instrument + asm 来对正在运行的java程序中的class类进行动态增强,可以在不用重启的情况下监控系统运行情况,方便的获取程序运行时的数据信息,如方法参数、返回值、全局变量和堆栈信息等,并且做到最少的侵入,占用最少的系统资源。
https://my.oschina.net/u/3705388/blog/1615376
有哪些常见的垃圾收集算法?
- 标记-清除算法:首先标记出所有需要回收的对象,然后统一回收所有被标记的对象;缺点是效率不高且容易产生大量不连续的内存碎片;
- 复制算法:将可用内存分为大小相等的两块,每次只使用其中一块;当这一块用完了,就将还活着的对象复制到另一块上,然后把已使用过的内存清理掉。在HotSpot里,考虑到大部分对象存活时间很短,将内存分为Eden和两块Survivor,默认比例为8:1:1。代价是存在部分内存空间浪费,且可能存在空间不够需要分配担保的情况,所以适合在新生代使用;
- 标记-整理算法:首先标记出所有需要回收的对象,然后让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。适用于老年代。
- 分代收集算法:一般把Java堆分新生代和老年代,在新生代用复制算法,在老年代用标记-清理或标记-整理算法,是现代虚拟机通常采用的算法。
CMS收集器
- 是一种以获取最短回收停顿时间为目标的收集器,特别适合互联网站或者B/S的服务端;
- 它是基于标记-清除 算法实现的,主要包括4个步骤:初始标记(STW,只是初始标记一下GC Roots能直接关联到的对象,速度很快)、并发标记(非STW,执行GC RootsTracing,耗时比较长)、重新标记(STW,修正并发标记期间因用户程序继续导致变动的那一部分对象标记)和并发清除(非STW,耗时较长);
- 还有3个明显的缺点:CMS收集器对CPU非常敏感(占用部分线程及CPU资源,影响总吞吐量)、无法处理浮动垃圾(默认达到92%就触发垃圾回收)、大量内存碎片产生(可以通过参数启动压缩);
G1收集器
- 一款面向服务端应用的垃圾收集器,后续会替换掉CMS垃圾收集器;
- 特点:
- 并行与并发(充分利用多核多CPU缩短STW时间)
- 分代收集(独立管理整个Java堆,但针对不同年龄的对象采取不同的策略)
- 空间整合(局部看是基于复制算法,从整体来看是基于标记-整理算法,都不会产生内存碎片)
- 可预测的停顿(可以明确指定在一个长度为M毫秒的时间片内垃圾收集不会超过N毫秒)
- 将堆分为大小相等的独立区域,避免全区域的垃圾收集;新生代和老年代不再物理隔离,只是部分Region的集合;
- G1跟踪各个Region垃圾堆积的价值大小,在后台维护一个优先列表,根据允许的收集时间优先回收价值最大的Region;
- Region之间的对象引用以及其他收集器中的新生代与老年代之间的对象引用,采用Remembered Set来避免全堆扫描;
- 分为几个步骤,和CMS的过程比较类似:
- 初始标记(标记一下GC Roots能直接关联的对象并修改TAMS值,需要STW但耗时很短)
- 并发标记(从GC Root从堆中对象进行可达性分析找存活的对象,耗时较长但可以与用户线程并发执行)
- 最终标记(为了修正并发标记期间产生变动的那一部分标记记录,这一期间的变化记录在Remembered
- Set Log里,然后合并到Remembered Set里,该阶段需要STW但是可并行执行)
- 筛选回收(对各个Region回收价值排序,根据用户期望的GC停顿时间制定回收计划来回收);
g1 和 cms 区别,吞吐量优先和响应优先的垃圾收集器选择
CMS是一种以最短停顿时间为目标的收集器
响应优先选择CMS,吞吐量高选择G1
JDK8:PermGen变更为MetaSpace详解
http://blog.csdn.net/lk7688535/article/details/51767460
java 堆内与堆外内存;java的栈内存和堆内存 (堆外内存)
http://blog.csdn.net/u012969412/article/details/76614988
如何进行性能调优以及常用的JDK的命令行工具有哪些?
- JVM调优:CPU使用率与Load值偏大(Thread count以及GC count)、关键接口响应时间很慢(GC time以及GC log中的STW的时间)、发生Full GC或者Old CMS GC非常频繁(内存泄露);
- JVM停顿(尽量避免Full GC、关闭偏向锁、输出GC日志到内存文件系统、关闭JVM输出的jstat日志);
- 将Java性能优化分为4个层级:应用层、数据库层、框架层、JVM层。每层优化难度逐级增加,涉及的知识和解决的问题也会不同。比如应用层需要理解代码逻辑,通过Java线程栈定位有问题代码行等;数据库层面需要分析SQL、定位死锁等;框架层需要懂源代码,理解框架机制;JVM 层需要对GC的类型和工作机制有深入了解,对各种 JVM 参数作用了然于胸;
- 围绕Java性能优化,有两种最基本的分析方法:现场分析法和事后分析法。现场分析法通过保留现场,再采用诊断工具分析定位。现场分析对线上影响较大,部分场景不太合适。事后分析法需要尽可能多收集现场数据,然后立即恢复服务,同时针对收集的现场数据进行事后分析和复现。
- OS 的诊断主要关注的是 CPU、Memory、I/O 三个方面。top、vmstat、 free –m、iostat;常用的Java应用诊断包括线程、堆栈、GC 等方面的诊断,可以使用jstack 、jstat、jmap;
jvm工具命令排查线上问题
http://blog.csdn.net/u010827436/article/details/46564641
你们的服务配置的虚拟机参数是怎么样的?
- -server --启用能够执行优化的编译器,显著提高服务器的性能
- -Xmx4000M --堆最大值
- -Xms4000M --堆初始大小
- -Xmn600M --年轻代大小
- -XX:PermSize=200M --持久代初始大小
- -XX:MaxPermSize=200M --持久代最大值
- -Xss256K --每个线程的栈大小
- -XX:+DisableExplicitGC --关闭System.gc()
- -XX:SurvivorRatio=1 --年轻代中Eden区与两个Survivor区的比值
- -XX:+UseConcMarkSweepGC --使用CMS内存收集
- -XX:+UseParNewGC --设置年轻代为并行收集
- -XX:+CMSParallelRemarkEnabled --降低标记停顿
- -XX:+UseCMSCompactAtFullCollection --在FULL GC的时候,对年老代进行压缩,可能会影响性能,但是可以消除碎片
- -XX:CMSFullGCsBeforeCompaction=0 --此值设置运行多少次GC以后对内存空间进行压缩、整理
- -XX:+CMSClassUnloadingEnabled --回收动态生成的代理类 SEE:http://stackoverflow.com/questions/3334911/what-does-jvm-flag-cmsclassunloadingenabled-actually-do
- -XX:LargePageSizeInBytes=128M --内存页的大小不可设置过大, 会影响Perm的大小
- -XX:+UseFastAccessorMethods --原始类型的快速优化
- -XX:+UseCMSInitiatingOccupancyOnly --使用手动定义初始化定义开始CMS收集,禁止hostspot自行触发CMS GC
- -XX:CMSInitiatingOccupancyFraction=80 --使用cms作为垃圾回收,使用80%后开始CMS收集
- -XX:SoftRefLRUPolicyMSPerMB=0 --每兆堆空闲空间中SoftReference的存活时间
- -XX:+PrintGCDetails --输出GC日志详情信息
- -XX:+PrintGCApplicationStoppedTime --输出垃圾回收期间程序暂停的时间
- -Xloggc:$WEB_APP_HOME/.tomcat/logs/gc.log --把相关日志信息记录到文件以便分析.
- -XX:+HeapDumpOnOutOfMemoryError --发生内存溢出时生成heapdump文件
- -XX:HeapDumpPath=$WEB_APP_HOME/.tomcat/logs/heapdump.hprof --heapdump文件地址
******* http://ginobefunny.com/post/jvm_interview_questions/
OOM定位排查 (用jmap看内存情况,然后用 jstack主要用来查看某个Java进程内的线程堆栈信息)
A.java.lang.OutOfMemoryError: Java heap space
最常见的原因:
1.应用程序需要的堆空间比JVM提供的大。 通过-Xmx5G 配置增加堆空间大小
2.流量/数据量峰值:应用程序在设计之初均有用户量和数据量的限制,某一时刻,当用户数量或数据量突然达到一个峰值,并且这个峰值已经超过了设计之初预期的阈值,那么以前正常的功能将会停止,并触发java.lang.OutOfMemoryError: Java heap space
异常。
3.内存泄漏:特定的编程错误会导致你的应用程序不停的消耗更多的内存,每次使用有内存泄漏风险的功能就会留下一些不能被回收的对象到堆空间中,随着时间的推移,泄漏的对象会消耗所有的堆空间,最终触发java.lang.OutOfMemoryError: Java heap space
错误。
2.3通过Debuggers, profilers, heap dump analyzers
等工具,可以让你的程序最大程度的避免内存泄漏问题。
B.java.lang.OutOfMemoryError:GC overhead limit exceeded
当应用程序花费超过98%的时间用来做GC并且回收了不到2%的堆内存时,会抛出java.lang.OutOfMemoryError:GC overhead limit exceeded
错误。具体的表现就是你的应用几乎耗尽所有可用内存,并且GC多次均未能清理干净。解决办法同上面2.3
C.java.lang.OutOfMemoryError: PermGen space
错误就表明持久代所在区域的内存已被耗尽。
原因是:太多的类或者太大的类被加载到permanent generation
(持久代)。
持久代主要存储的是每个类的信息,比如:类加载器引用、运行时常量池(所有常量、字段引用、方法引用、属性)、字段(Field)数据、方法(Method)数据、方法代码、方法字节码等等。我们可以推断出,PermGen
的大小取决于被加载类的数量以及类的大小。
-XX:MaxPermSize参数 设置持久带大小
分析dump文件:首先,找出引用在哪里被持有;其次,给你的web应用程序添加一个关闭的hook,或者在应用程序卸载后移除引用。你可以使用如下命令导出dump文件:
jmap -dump:format=b,file=dump.hprof <process-id>
当你拿到生成的堆转储文件,并利用像Eclipse Memory Analyzer Toolkit这样的工具来寻找应该卸载却没被卸载的类加载器,然后对该类加载器加载的类进行排查,找到可疑对象,分析使用或者生成这些类的代码,查找产生问题的根源并解决它。
https://www.jianshu.com/p/2fdee831ed03
https://www.cnblogs.com/itar/p/7424311.html
https://www.cnblogs.com/wbyp/p/7753528.html
http://blog.csdn.net/garfielder007/article/details/55822985