jvm调优(3):各种内存溢出分析与建议

硬件和基础信息





StackOverflowError,StackOverflowError问题导向

Java虚拟机规范中描述了两种异常

如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。

如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。


1.单线程栈溢出





定义了大量的本地变量,增大此方法帧中本地变量表的长度。结果:抛出StackOverflowError异常时输出的堆栈深度相应缩小



2.多线程栈溢出





操作系统分配给每个进程的内存是有限制的,譬如32位的Windows限制为2GB。虚拟机提供了参数来控制Java堆和方法区的这两部分内存的最大值。剩余的内
存为2GB(操作系统限制)减去Xmx(最大堆容量),再减去MaxPermSize(最大方法区容量),程序计数器消耗内存很小,可以忽略掉。如果虚拟机进程本身耗费的内存不计算在内,剩下的内存就由虚拟机栈和本地方法栈“瓜分”了。每个线程分配到的栈容量越大,可以建立的线程数量自然就越少,建立线程时就越容易把剩下的内存耗尽

  
32位和64位JDK有什么区别呢? 
JVM 32bit 和JVM 64bit的区别如下: 
  1目前只有server VM支持64bit JVM,client不支持32bit JVM。 
  2 .The Java Plug-in, AWT Robot and Java Web Start这些组件目前不支持64bit JVM 
  3.本地代码的影响:对JNI的编程接口没有影响,但是针对32-bit VM写的代码必须重新编译才能在64-bit VM工作。 
  4.32-bit JVM堆大小最大是4G, 64-bit VMs 上, Java堆的大小受限于物理内存和操作系统提供的虚拟内存。(这里的堆并不严谨) 
  5.线程的默认堆栈大小:在windows上32位JVM,默认堆栈最大是320k 64-bit JVM是1024K。 
  6.性能影响: 
    (1)64bit JVM相比32bit JVM,在大量的内存访问的情况下,其性能损失更少,AMD64和EM64T平台在64位模式下运行时,Java虚拟机得到了一些额外的寄存器,它可以用来生成更有效的原生指令序列。 
    (2)性能上,在SPARC 处理器上,当一个java应用程序从32bit 平台移植到64bit平台的64bit JVM会用大约 10-20%的性能损失,而在AMD64和 EM64T平台上,其性能损失的范围在0-15%. 
以上摘自http://java.sun.com/docs/hotspot/HotSpotFAQ.html#64bit_description 


Physical Memory Limits: Windows 7

The following table specifies the limits on physical memory for Windows 7.

Version Limit on X86 Limit on X64
Windows 7 Ultimate

4 GB

192 GB

Windows 7 Enterprise

4 GB

192 GB

Windows 7 Professional

4 GB

192 GB

Windows 7 Home Premium

4 GB

16 GB

Windows 7 Home Basic

4 GB

8 GB

Windows 7 Starter

2 GB

N/A


在开发多线程的应用时特别注意,出现StackOverflowError异常时有错误堆栈可以阅读,相对来说,比较容易找到问题的所在。而且,如果使用虚拟机默认参数,栈
深度在大多数情况下(因为每个方法压入栈的帧大小并不是一样的,所以只能说在大多数情况下)达到1000~2000完全没有问题,对于正常的方法调用(包括递归),这个深度应该完全够用了。但是,如果是建立过多线程导致的内存溢出,在不能减少线程数或者更换64位虚拟机的情况下,就只能通过减少最大堆和减少栈容量来换取更多的线程(以上案例验证增加栈容量200m满足异常条件)。如果没有这方面的处理经验,这种通过“减少内存”的手段来解决内存溢出的方式会比较难以想到



3 运行时常量池导致的内存溢出异常

 


java.lang.OutOfMemoryError:PermGenspace:这种是P区内存不够,可通过调整JVM的配置:
-XX:MaxPermSize=128m
-XXermSize=128m
【注】:

方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等
JVM的Perm区主要用于存放Class和Meta信息的,Class在被Loader时就会被放到Perm Genspace,这个区域成为年老代,GC在主程序运行期间不会对年老区进行清理,默认是64M大小,当程序需要加载的对象比较多时,超过64M就会报这部分内存溢出了,需要加大内存分配,一般128m足够


4  直接内存溢出



其他情形还很多,未必都行得通,主要由硬件、基础环境配置、版本和应用程序实际情况而定,下面仅供参考---------------------------------------------

java.lang.OutOfMemoryError:Javaheapspace

这种是java堆内存不够,一个原因是真不够(如递归的层数太多等),另一个原因是程序中有死循环;
  如果是java堆内存不够的话,可以通过调整JVM下面的配置来解决:
  -Xms3062m
  -Xmx3062m


java.lang.OutOfMemoryError:GCoverheadlimitexceeded
  【解释】:JDK6新增错误类型,当GC为释放很小空间占用大量时间时抛出;一般是因为堆太小,导致异常的原因,没有足够的内存。
  【解决方案】:
  1、查看系统是否有使用大内存的代码或死循环;
  2、通过添加JVM配置,来限制使用内存:
       -XX:-UseGCOverheadLimit


java.lang.OutOfMemoryError:Directbuffermemory
  调整-XX:MaxDirectMemorySize=参数,如添加JVM配置:
  -XX:MaxDirectMemorySize=128m

        这个和第4种情况类似,但场景可能不同


java.lang.OutOfMemoryError:unabletocreatenewnativethread
  【原因】:Stack空间不足以创建额外的线程,要么是创建的线程过多,要么是Stack空间确实小了。
  【解决】:由于JVM没有提供参数设置总的stack空间大小,但可以设置单个线程栈的大小;而系统的用户空间一共是3G,除了Text/Data/BSS/MemoryMapping几个段之外,Heap和Stack空间的总量有限,是此消彼长的。因此遇到这个错误,可以通过两个途径解决:1.通过-Xss启动参数减少单个线程栈大小,这样便能开更多线程(当然不能太小,太小会出现StackOverflowError);2.通过-Xms-Xmx两参数减少Heap大小,将内存让给Stack(前提是保证Heap空间够用)。


java.lang.StackOverflowError
  【原因】:这也内存溢出错误的一种,即线程栈的溢出,要么是方法调用层次过多(比如存在无限递归调用),要么是线程栈太小。
  【解决】:优化程序设计,减少方法调用层次;调整-Xss参数增加线程栈大小。


参考

http://zyslovely.iteye.com/blog/1464820

https://msdn.microsoft.com/en-us/library/aa366778%28v=vs.85%29.aspx#memory_limits

http://blog.csdn.net/youthon/article/details/47042641


猜你喜欢

转载自blog.csdn.net/luozhonghua2014/article/details/78859570
今日推荐