JVM调优及常见解决OOM
JVM调优及常见的OOM解决
JVM调优
JVM具体调优参数这篇文章讲得很详细
GC日志分析
获取日志,命令获取和配置jvm的GC日志输出两种方式。
常用命令:jstat-gc 统计垃圾回收堆的行为
JVM调优命令
jps:显示虚拟机进程
jstat:监视虚拟机运行时状态信息,类装载、内存、垃圾收集、jit即时编译器等运行数据
jmap:生成heap dump文件
jhat:搭配 jmap使用,分析dump文件。
jstack: 生成当前虚拟机当前时刻的快照。
jinfo :实时查看和调整虚拟机运行参数。
调优工具
jdk自带的监控工具:jconsole和jvusualvm
第三方:MAT(Memory Analyzer Tool)、GChIsto
内存泄漏和内存溢出
内存泄漏和内存溢出的区别,它俩是两个完全不同的概念, 但它们之间存在一些关联。
- 内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现 out of memory;
- 内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
所以内存泄漏可能会导致内存溢出,但内存溢出并不完全都是因为内存泄漏,也有可能使用了太多的大对象导致。
Vector v=new Vector(10);
for (int i=1;i<100; i++)
{
Object o=new Object();
v.add(o);
o=null;//释放本身并没有用,因为v还保持着对object的引用,gc回收不了。
}
//此时,所有的Object对象都没有被释放,因为变量v引用这些对象。
//最简单的解决方法是,在v对象使用完后,把v对象释放,也就释放了object
v=null;
常见的OOM解决
堆溢出
最为常见的OOM情况,报错信息:
java.lang.OutOfMemoryError: Java heap space
原因:
1、代码中存在大对象的分配。
2、可能存在内存泄露,导致频繁gc后还是无法找到一块足够大的内存存储当前对象。
解决方法:
1、
①手动使用命令jmap获取Dump内存信息。
②设置JVM在OOM时自动Dump 内存信息日志以及自动打印GC日志。
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\study\log_hprof\gc.hprof
-XX:+PrintGCDetails -Xloggc:D:\study\log_gc\gc.log
然后可以用MAT工具分析gc.hprof文件,查看堆内存详细信息。
2、查看代码中是否有超长数组的内存分配。
3、查看代码中是否过度使用终结器Finalizer,导致对象没有立即释放。
4、检查系统是否超出预期访问量/数据量。
5、如果没有找到内存泄漏,可以使用-Xmx增加jvm堆内存。
Gc overhead limit exceeded
报错代码为:
java.lang.OutOfMemoryError:GC overhead limit exceeded
原因:当进程花费了98%以上的时间执行gc,但是只恢复了不到2%的内存,且该动作连续重复执行了5次,就会抛出此异常。简单的说,就是应用程序耗尽了所有可用的内存,GC也无法回收。
解决方法:和堆内存溢出解决方式相似。
永久代/元空间溢出
报错代码为:
java.lang.OutOfMemoryError: PermGen spacejava.lang.OutOfMemoryError: Metaspace
原因:该错误代表永久带/元空间已用满通常是加载的class数量太多,或者是体积过大。永久代存放了被虚拟机加载的类信息、常量、静态变量、JIT编译后的代码等。
解决方法:
1、检查是否永久代或元空间设置过小
2、检查代码中是否存在大量的反射操作
3、使用jmap后检查是否存在大量由于反射生成的代理类
4、重启JVM
unable to create new native thread
报错信息
java.lang.OutOfMemoryError : unable to create new native Thread
原因:每个线程都需要占用一定的内存空间,当JVM向操作系统请求创建一个naive线程时,如果没有足够的资源分配,就会报此错。
线程数超过操作系统最大数限制
native内存空间不足
解决方法:
限制线程池大小
升级配置,为机器提供更多内存
修复程序的线程池内存泄漏的问题
栈溢出
报错:
java.lang.StackOverflowError
最直接的原因就是方法的递归调用导致的。
{
public static void main(String[] args)
{
new Stack().test();
}
public void test()
{
test();
}
}
底层原因:栈。栈由一系列的栈帧组成,栈帧保存每一个方法的局部变量、操作数栈、常量池指针,每一次方法的调用创建一个栈帧,用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法调用直至执行完毕的过程就对应一个帧在栈中的入栈到出栈的过程。
如果无限递调用方法,线程请求的栈深度太深,超出了虚拟机所允许的深度,就会出现StackOverFlowError(因为每一层帧都占用一定空间,超出这个值就会报错)
浙公网安备 33010602011771号