性能分析-内存溢出 及 内存泄漏

2022-11-16  本文已影响0人  star_he

区别


内存溢出 out of memory:是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如说给你一个int类型的存储数据大小的空间,但是你却拿来存放long类型的数据,这样就会导致内存溢出。

内存泄露 memory leak:是指程序在申请内存后,无法释放已申请的内存空间。通俗来说,即“占着茅坑不拉屎”。例如说,你家公司厕所坑位一共5个,你每天早上一来到公司,就先过去占着一个,在里面摸鱼刷抖音,这时候还剩下4个,还有可用的空间,所以一次内存泄露危害可以忽略。但是你的好基友张三、李四、王五、马六也来了,跟你一样在茅厕里摸鱼刷抖音。那些排队等待拉~的人就没有地方宣泄,内存泄漏也是这样。

memory leak会最终会导致out of memory:从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到。

什么时候会抛出OutOfMemoryException?(OOM)


引起内存溢出的原因有很多种,常见的有以下几种:

1、java.lang.OutofMemoryError:Java heap space:堆溢出

Java堆空间不够,当应用程序申请更多的内存,而Java堆内存已经无法满足应用程序对内存的需要,将抛出这种异常,这种是java堆内存不够,一个原因是真不够,另一个原因是程序中有死循环,可以通过调整JVM的配置来解决。

2、java.lang.OutofMemoryError:PermGen space:持久代溢出

Java永久代空间不够,永久代中包含类的字节码和长常量池,类的字节码加载后的信息,这和存放对象实例的堆区是不同的,大多数JVM的实现都不会对永久带进行垃圾回收,因此,只要类加载的过多就会出现这个问题。一般的应用程序都不会产生这个错误,然而,对于Web服务器来讲,会产生有大量的JSP,JSP在运行时被动态的编译成Java Servlet类,然后加载到方法区,因此,太多的JSP的Web工程可能产生这个异常。

这种是P区内存不够,可通过调整JVM的配置

JVM的Perm区主要用于存放Class和Meta信息的,Class在被Loader时就会被放到PermGen space,这个区域成为年老代,GC在主程序运行期间不会对年老区进行清理,默认是64M大小,当程序需要加载的对象比较多时,超过64M就会报这部分内存溢出了,需要加大内存分配,一般128m足够。 

3、java.lang.OutofMemoryError:unable to create new native thread

本质原因是创建了太多的线程,而能创建的线程数是有限制的,导致了这种异常的发生。也可能是Stack空间确实小了。

由于JVM没有提供参数设置总的stack空间大小,但可以设置单个线程栈的大小;而系统的用户空间一共是3G,除了Text/Data/BSS /MemoryMapping几个段之外,Heap和Stack空间的总量有限,是此消彼长的。因此遇到这个错误,可以通过两个途径解决: 

  1.通过 -Xss启动参数减少单个线程栈大小,这样便能开更多线程(当然不能太小,太小会出现StackOverflowError); 

  2.通过-Xms -Xmx 两参数减少Heap大小,将内存让给Stack(前提是保证Heap空间够用)。 

4、java.lang.OutofMemoryError:GC overhead limit exceeded

当 Java 进程花费 98% 以上的时间执行 GC,但只恢复了不到 2% 的内存,且该动作连续重复了 5 次,就会抛出 java.lang.OutOfMemoryError:GC overhead limit exceeded 错误。简单地说,就是应用程序已经基本耗尽了所有可用内存, GC 也无法回收。

    可以先查看系统是否有使用大内存的代码或死循环 或者 通过添加JVM配置,来限制使用内存。

5、 java.lang.StackOverflowError : Thread Stack space:栈溢出

栈溢出了,JVM依然是采用栈式的虚拟机,这个和C和Pascal都是一样的。函数的调用过程都体现在堆栈和退栈上了。调用构造函数的 “层”太多了,以致于把栈区溢出了。 通常来讲,一般栈区远远小于堆区的,因为函数调用过程往往不会多于上千层,而即便每个函数调用需要 1K的空间(这个大约相当于在一个C函数内声明了256个int类型的变量),那么栈区也不过是需要1MB的空间。通常栈的大小是1-2MB的。通俗一点讲就是单线程的程序需要的内存太大了。 通常递归也不要递归的层次过多,很容易溢出。

1:修改程序。2:通过 -Xss: 来设置每个线程的Stack大小即可。

JVM内存分析过程


下面有一个分析过程的案例,写的非常详细,推荐:https://blog.csdn.net/zhanghan18333611647/article/details/109255980

内存分析过程和CPU分析过程思路相似:进程->线程->查看线程资源->分析线程代码。

上一篇下一篇

猜你喜欢

热点阅读