内存溢出

内存溢出

内存溢出是指应用新建一个对象实例时,所需的内存空间大于堆的可用空间。内存溢出的种类较多,一般会在报错日志里看到OutOfMemoryError关键字。

系统内存分配

首先看下操作系统如何划分内存给应用系统,其实在Win 32Linux 32的系统中,地址总线为32位的理论上应该可以支持4G内存空间,但是当你在Win 32上设置初始化内存如果达到2G,就会报错,说这个块空间没法做。

首先默认的Win32系统,会按照50%比例给予给Kernel使用,而另一部分给应用内存,也就是说操作系统内核部分不论是否使用,这一半是不会给你的,而还有2G呢,它在系统扩展的部分,也就是并非Kernel的部分,有很多静态区域 和字典表的内容,所以要划分一个连续的2G内存给JVMWin 32上是不可能的,Win 32提出了一种Win 32 3G模式,貌似可以划分3G空间,其实它只是将内核部分缩小也就是管理部分缩小,也就是将一部分划分到外部来使用,而且Win 32习惯在内存2G的位置做一些手脚,让你分配连续2G没有可能性,一般来说在Win 32平台上,在物理内存足够的情况下给JVM划分的空间一般是1.4 ~ 1.5G左右,具体数据没有测试过;而Linux 32类似于Win 32 3G模式,但是它还是一般情况下分布不凌乱的情况下,一般可以给JVM划分到2G的大小。Linux 32 Hugemem是一个扩展版本,可以划分更大的空间,但是需要付出一些其他的代价,理论上可以支持到4G给应用,也就是Kenel是独立 的;Solaris x86-32AIX 32等系统,也类似于Linux 32平台一样。

当你申请一个线程的时候,它的除了线程内部对象的开销外,线程本身的开销,是需要OS来调度完成,一般来说,会在OS的线程与虚拟机内部有都有一个一一对 应的,但是会根据操作系统不同有所变化,有些可能只有一个,总之heapSize外的那部分空间是跑不掉的,它放在哪里呢?就是放在Stack中的,所以 上文中的-Xss就是设置这个的,在jdk 1.5以后,每个线程的大小被默认设置为1Mstack开销,我们习惯将这个开销降低。

常见内存溢出

常见内存溢出种类及分析思路如下:

  • java.lang.OutOfMemoryError: Java heap space。原因:堆中(新生代和老年代)无法继续分配对象了、某些对象的引用长期被持有没有被释放,垃圾回收器无法回收、使用了大量的Finalizer对象,这些对象并不在GC的回收周期内等。一般堆溢出都是由于内存泄漏引起的,如果确认没有内存泄漏,可以适当通过增大堆内存。

  • java.lang.OutOfMemoryError: GC overhead limit exceeded。原因:垃圾回收器超过98%的时间用来垃圾回收,但回收不到2%的堆内存,一般是因为存在内存泄漏或堆空间过小。

  • java.lang.OutOfMemoryError: Metaspacejava.lang.OutOfMemoryError: PermGen space。排查思路:检查是否有动态的类加载但没有及时卸载,是否有大量的字符串常量池化,永久代/元空间是否设置过小等。

  • java.lang.OutOfMemoryError: unable to create new native Thread。原因:虚拟机在拓展栈空间时,无法申请到足够的内存空间。可适当降低每个线程栈的大小以及应用整体的线程个数。此外,系统里总体的进程/线程创建总数也受到系统空闲内存和操作系统的限制,请仔细检查。注:这种栈溢出,和StackOverflowError不同,后者是由于方法调用层次太深,分配的栈内存不够新建栈帧导致。

此外,还有Swap分区溢出、本地方法栈溢出、数组分配溢出等OutOfMemoryError类型。

内存溢出工具

使用非常多的工具区检测Java的内存如:jstat(只能看HeapSizePermSize)jmap(很细的东西)jps(javaps -ef呵呵)jdb(这个不是监控工具哈,这个是debug工具)jprofile(图形支持,但是可以远程连接)等等;jconsole(可以看到heapsizepermsize+native mem size(这这里叫做:non-heapsize)等等的使用的趋势图)visualvm(极为推荐的东西,图形化查看,你可以查看到内存单元分配、交 换、回收、移动等等整个过程,非常清晰展现jvm的全局资源)、另外pmap可以展现非常清晰的资料,可以精确到某一个java进程内部的每一个细节,而 且可以看到heapsize只是其中很小一部分(solaris操作系统上看得最齐全,LINUX下有些进程可能看不太懂);也可以在/proc/进程 号/maps中查看(这里可以看到内存地址单元的起始地址,包含了reserved的地址范围和commited的地址范围),全局资源使用操作系统top命令和free命令看;IBM有一个GCMV免费下载工具也很好;Win32有一个WMMap工具都是很好的工具

上一页