「javaoom排查」java oom排查思路
本篇文章给大家谈谈javaoom排查,以及java oom排查思路对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。
本文目录一览:
- 1、如何定位OutOfMemory的根本原因
- 2、记一次jvm堆外内存OOM的解决过程
- 3、如何检查和解决java虚拟机内存溢出的问题
- 4、jvm故障排查
- 5、记录一次MetaSpace OOM问题排查历程
- 6、java oom异常怎么解决方案
如何定位OutOfMemory的根本原因
分析工具
1) 动态分析工具
jprofile
2) 静态分析工具
a: 在启动java的时候加上参数-xx:+heapdumponoutofmemoryerror,这样如果由于oom导致jvm crash的时候可以便于我们分析,生成的heap dump文件名字的命名规范如下, java_pidxxxx.hprof
b: 工具1 elcipsemat
2 ibm heap ana java -xmx1600 -jar ha396.jar
2 java 内存机制和exception实例
1对于从事c、c++程序开发的开发人员来说,担负着每一个对象生命开始到终结的维护责任。
对于java程序员来说,不需要在为每一个new操作去写配对的delete/free,不容易出现内容泄漏和内存溢出错误。不过,也正是因为java程序员把内存控制的权力交给了jvm,一旦出现泄漏和溢出,如果不了解jvm是怎样使用内存的,那排查错误将会是一件非常困难的事情。下面介绍一下java出现的oom有关的 exception和可能出现的方式
a exception in thread "main" java.lang.outofmemoryerror: permgen space
public static void main(string[] args) {
//使用list保持着常量池引用,压制full gc回收常量池行为
liststring list = new arrayliststring();
// 10m的permsize在integer范围内足够产生oom了
int i = 0;
while (true) {
list.add(string.valueof(i++).intern());
}
}
这一部分用于存放class和meta的信息,class在被 load的时候被放入permgen space区域(包括常量池:静态变量),它和和存放instance的heap区域不同,gc(garbage collection)不会在主程序运行期对permgen space进行清理,所以如果你的app会load很多class的话,就很可能出现permgen space错误动态生成的类,加载如spring、hibernate对类进行增强时,都会使用到cglib这类字节码技术,
此文来自: 马开东博客 转载请注明出处 网址:
当增强的类越多,就需要越大的方法区用于保证动态生成的class可以加载入内存。
b java.lang.outofmemoryerror: java heap space,被缓存的实例(cache)对象,大的map,list引用大的对象等等,都会保存于此
public static void main(string[] args) {
liststring list = new arrayliststring();
int i = 0;
while (true) {
list.add(new string(“test”));
}
}
c exception in thread "main" java.lang.stackoverflowerror
栈帧太多,也就是函数调用层级过多)导致。检查是否有死递归的情况~
/**
* vm args:-xss128k
*/
public class javavmstacksof {
private int stacklength = 1;
public void stackleak() {
stacklength++;
stackleak();
}
public static void main(string[] args) throws throwable {
javavmstacksof oom = new javavmstacksof();
try {
oom.stackleak();
} catch (throwable e) {
system.out.println("stack length:" + oom.stacklength);
throw e;
}
}
}
记一次jvm堆外内存OOM的解决过程
本文记录一次堆外内存OOM的排查过程。
周末的时候同事对线上服务进行了一次扩容,本以为只是简单增加几个实例而已,结果新实例接入流量后疯狂报警,只能马上切换下线,查看日志后发现如下错误:
至此堆外内存使用量过高的问题解决了,那么fullgc过多的问题是怎么回事呢,线上的gc日志是打开的,拉下来一份看了下,如下图所示:
查看了jdk的源码发现 ByteBuffer#allocateDirect 的过程中确实有一个 System.gc() 的调用,对比上边图1的调用栈可以确定是这里触发的fgc,也就是说fgc的问题其实是堆外内存OOM的副产品,堆外内存OOM的问题解决这个问题也会迎刃而解,至此问题原因排查清楚了。
那么为什么老实例和新实例会表现不一样呢,线上的实例并没有出现OOM的错误日志呢,问题的关键在于存储服务的client初始化后便不再变动,新实例初始化时比旧实例的连接数多,恰好到达了6G堆外内存上限附近,而netty的堆外内存是动态申请的,初始化时机比前者晚,请求进来时再申请堆外内存时没有可用的堆外内存了,触发了fgc,最终抛出了OOM。
如何检查和解决java虚拟机内存溢出的问题
一,jvm内存区域
1, 程序计数器
一块很小的内存空间,作用是当前线程所执行的字节码的行号指示器。
2, java栈
与程序计数器一样,java栈(虚拟机栈)也是线程私有的,其生命周期与线程相同。通常存放基本数据类型,对象引用(一个指向对象起始地址的引用指针或一个代表对象的句柄),reeturnAddress类型(指向一条字节码指令的地址)
栈区域有两种异常类型:如果线程请求的栈深度大于虚拟机所允许的深度,将抛StrackOverflowError异常;如果虚拟机栈可以动态扩展(大部分虚拟机都可动态扩展),当扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常。
3, 本地方法栈
与虚拟机栈作用很相似,区别是虚拟机栈为虚拟机执行java方法服务,而本地方法栈则是为虚拟机用到的Native方法服务。和虚拟机栈一样可能抛出StackOverflowError和OutOfMemoryError异常。
4, java堆
java
Heap是jvm所管理的内存中最大的区域。JavaHeap是被所有线程共享的一块内存区域,在虚拟机启动时创建。主要存放对象实例。JavaHeap
是垃圾收集器管理的主要区域,其可细分为新生代和老年代。如果在堆中没有内存完成实例分配,并且也无法再扩展时,会抛出OutOfMemoryError
异常。
5, 方法区
与javaHeap一样是各个线程共享的内存区域,用于存放已被虚拟机加载的类信息、常量、静态变量、及时编译器编译后的代码等数据。当方法区无法满足内
存分配的需求时,将抛出OutOfMemoryError异常。方法同时包含常听说的运行时常量池,用于存放编译期生成的各种字面量和符号引用。
6, 直接内存
直接内存并不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域,是jvm外部的内存区域,这部分区域也可能导致OutOfMemoryError异常。
二,jvm参数
-Xss(StackSpace)栈空间
-Xms ,-Xmx(heap memory
space)堆空间:Heap是大家最为熟悉的区域,他是jvm用来存储对象实例的区域,Heap在32位的系统中最大为2G,其大小通过-Xms和
-Xmx来控制,-Xms为jvm启动时申请的最小Heap内存,默认为物理内存的1/64,但小于1G,-Xmx为jvm可申请的最大的Heap内存,
默认为物理内存的1/4,一般也小于1G,默认当空余堆内存小于40%时,jvm会最大Heap的大小到-Xmx指定大小,可通过
-XX:MinHeapFreeRatio来指定这个比例,当空余堆内存大于70%时,JVM会将Heap的大小往-Xms指定的大小调整,可通过
-XX:MaxHeapFreeRatio来指定这个比例,但通常为了避免频繁调整HeapSize的大小,将-Xms和-Xmx的值设为相同。
-XX:PermSize -XX:MaxPermSize :方法区持久代大小: 方法区域也是全局共享的,在一定的条件下它也会被 GC ,当方法区域需要使用的内存超过其允许的大小时,会抛出 OutOfMemory 的错误信息。
三,常见内存溢出错误解决办法
1, OutOfMemoryError异常
除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError(OOM)异常的可能,
Java Heap 溢出
一般的异常信息:java.lang.OutOfMemoryError:Java heap spacess
java堆用于存储对象实例,我们只要不断的创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,就会在对象数量达到最大堆容量限制后产生内存溢出异常。
出现这种异常,一般手段是先通过内存映像分析工具(如Eclipse Memory
Analyzer)对dump出来的堆转存快照进行分析,重点是确认内存中的对象是否是必要的,先分清是因为内存泄漏(Memory
Leak)还是内存溢出(Memory Overflow)。
如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链。于是就能找到泄漏对象时通过怎样的路径与GC Roots相关联并导致垃圾收集器无法自动回收。
如果不存在泄漏,那就应该检查虚拟机的参数(-Xmx与-Xms)的设置是否适当。
2, 虚拟机栈和本地方法栈溢出
如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。
如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常
这里需要注意当栈的大小越大可分配的线程数就越少。
3, 运行时常量池溢出
异常信息:java.lang.OutOfMemoryError:PermGen space
如果要向运行时常量池中添加内容,最简单的做法就是使用String.intern()这个Native方法。该方法的作用是:如果池中已经包含一个等于
此String的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并且返回此String
对象的引用。由于常量池分配在方法区内,我们可以通过-XX:PermSize和-XX:MaxPermSize限制方法区的大小,从而间接限制其中常量
池的容量。
4, 方法区溢出
方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。
异常信息:java.lang.OutOfMemoryError:PermGen space
方法区溢出也是一种常见的内存溢出异常,一个类如果要被垃圾收集器回收,判定条件是很苛刻的。在经常动态生成大量Class的应用中,要特别注意这点。
jvm故障排查
背景:
服务在正常运营中,偶尔出现进程被杀死的情况,所以总结一下排查问题的方法
1.应用日志
如:根据配置的logback-spring,logback配置的日志路径下去寻找
2.容器日志
如:tomcat崩溃,去catalina.201X-XX-XX.log,localhost.201X-XX-XX.log等容器配置的日志文件中寻找
3.JVM奔溃日志
当JVM发生致命错误导致崩溃时,会生成一个hs_err_pid_xxx.log这样的文件,该文件包含了导致 JVM crash 的重要信息,我们可以通过分析该文件定位到导致 JVM Crash 的原因,修复
默认情况下,该文件是生成在工作目录下的,当然也可以通过 JVM 参数指定生成路径:
-XX:ErrorFile=/var/log/hs_err_pid.log
4.系统日志
(1)系统报错日志:/var/log/messages
top查看资源占用情况(M排序由高到低)这是实时的参考率不大
OOM killer(Out Of Memory killer),这个东西会在系统内存耗尽的情况下跳出来,选择性的干掉一些进程以求释放一些内存
OOM killer是通过/proc/pid/oom_score这个值来决定哪个进程被干掉的。这个值是系统综合进程的内存消耗量、CPU时间(utime + stime)、存活时间(uptime - start time) 和oom_adj计算出的,消耗内存越多分越高,存活时间越长分越低。总之,总的策略是:损失最少的工作,释放最大的内存同时不伤及无辜的用了很大内存的进程,并且杀掉的进 程数尽量少。
我们可以开启dump core 和 启动配置内存泄露的jvm参数获取dump文件的方式获取错误资料,core和dump是可以互转的
1)dumo core
ulimit -c unlimited(开启)
在/usr/local/apps/获取core文件
gdb java core分析
2)dump
内存泄漏是指本应该被GC回收的无用对象没有被回收,导致的内存空间的浪费,当内存泄露严重时会导致OOM
启动时配置jvm参数
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/xx/java.hprof;
注:+HeapDumpOnOutOfMemoryError 需要放在-jar前面
如java -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=/xx/java.hprof -jarxx-1.0-SNAPSHOT.jar
出现异常,可以去找配置路径的dump快照文件,接下来借助VisualVM这种可视化工具分析就行,定位问题。
(2)内核日志:dmesg
去内核日志里头查询。有时Linux系统或者系统上运行的java或者其它进程,会发生一些莫名其妙的问题,比如突然挂掉了,比如突然重启等等。在软件上找不到问题所在,此时 我们应该怀疑硬件或者内核的问题,此时我们就可以使用 dmesg来查看:
dmesg | grep java
输出如下
记录一次MetaSpace OOM问题排查历程
首先是监控告警。随后查看监控发现 cpu飙升
然后此台应用挂掉
1、日志提醒 metaspace oom,并且频繁出现
1、使用jdk命令:jmap -clstats pid 打印类加载信息,显示有较多:com.alibaba.fastjson.util.ASMClassLoader
其实easyopen框架在@Api注解上已经提供了wrapResult = true/false的能力。
java oom异常怎么解决方案
在 Java中,JavaVM拥有自动管理内存的功能,Java的GC能够进行垃圾回收,但是Android中如果ImageView使用过多的Bitmap的话,经常会报OOM(内存溢出)。
造成内存溢出及解决方案:
1.使用BitmapFactory.decodeStream替代createBitmap方法
原因是该方法直读取图片字节,调用JNInativeDecodeAsset()来完成decode,无需再使用java层的createBitmap。
2.使用压缩读取技术
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imageSdUri, options);
final int height = options.outHeight;
final int width = options.outWidth;
options.inSampleSize = 1;
int w = 320;
int h = 480;
h = w*height/width;//计算出宽高等比率
int a = options.outWidth/ w;
int b = options.outHeight / h;
options.inSampleSize = Math.max(a, b);
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(imageSdUri, options);
3.及时释放Bitamp
Bitmap对象在不使用时,我们应该先调用recycle()释放内存,然后才它设置为null.虽然recycle()从源码上看,调用它应该能立即释放Bitmap的主要内存,但是测试结果显示它并没能立即释放内存。但是我它应该还是能大大的加速Bitmap的主要内存的释放。
javaoom排查的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于java oom排查思路、javaoom排查的信息别忘了在本站进行查找喔。