`

JAVA内存溢出分为两种情况

    博客分类:
  • Java
 
阅读更多

JAVA 内存溢出分为两种情况:
一种是内存足够时
JVM堆内存是足够的,但只是没有连续的内存空间导致,比如申请连续内存空间的数组:
Java代码 
String[] array = new String[10000]。 
String[] array = new String[10000]。
还有一种是某种原因导致的内存不足而产出内出溢出。
比如,内存泄露导致的内存溢出。内存泄露时指存在无用的对象不能被GC回收,一般情况下不会出现问题。但是如果存在大量GC不能回收的无用对象后,会导致堆内存严重不足,而产生内存泄露。
这里详细讲述第二种内存溢出的情况。
一、内存溢出类型
1、java.lang.OutOfMemoryError: PermGen space
JVM管理两种类型的内存,堆和非堆。堆是给开发人员用的上面说的就是,是在JVM启动时创建;非堆是留给JVM自己用的,用来存放类的信息的。 它和堆不同,运行期内GC不会释放空间。如果web app用了大量的第三方jar或者应用有太多的class文件而恰好MaxPermSize设置较小,超出了也会导致这块内存的占用过多造成溢出,或者 tomcat热部署时侯不会清理前面加载的环境,只会将context更改为新部署的,非堆存的内容就会越来越多。

PermGen space的全称是Permanent Generation space,是指内存的永久保存区域,这块内存主要是被JVM存放Class和Meta信息的,Class在被Loader时就会被放到PermGen space中,它和存放类实例(Instance)的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的应用中有很CLASS的话,就很可能出现PermGen space错误,这种错误常见在web服务器对JSP进行pre compile的时候。如果你的WEB APP下都用了大量的第三方jar, 其大小超过了jvm默认的大小(4M)那么就会产生此错误信息了。

一个最佳的配置例子:(经过本人验证,自从用此配置之后,再未出现过tomcat死掉的情况)
set JAVA_OPTS=-Xms800m -Xmx800m -XX:PermSize=128M -XX:MaxNewSize=256m -XX:MaxPermSize=256m

2、java.lang.OutOfMemoryError: Java heap space
第一种情况是个补充,主要存在问题就是出现在这个情况中。其默认空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)是物理内存的1 /4。如果内存剩余不到40%,JVM就会增大堆到Xmx设置的值,内存剩余超过70%,JVM就会减小堆到Xms设置的值。所以服务器的Xmx和Xms 设置一般应该设置相同避免每次GC后都要调整虚拟机堆的大小。假设物理内存无限大,那么JVM内存的最大值跟操作系统 有关,一般32位机是1.5g到3g 之间,而64位的就不会有限制了。

注意:如果Xms超过了Xmx值,或者堆最大值和非堆最大值的总和超过了物理内存或者操作系统的最大限制都会引起服务器启动不起来。

垃圾回收GC的角色

JVM调用GC的频度还是很高的,主要两种情况下进行垃圾回收:

当应用程序线程空闲;另一个是java内存堆不足时,会不断调用GC,若连续回收都解决不了内存堆不足的问题时,就会报out of memory错误。因为这个异常根据系统运行环境决定,所以无法预期它何时出现。

根据GC的机制,程序的运行会引起系统运行环境的变化,增加GC的触发机会。

为了避免这些问题,程序的设计和编写就应避免垃圾对象的内存占用和GC的开销。显示调用System.GC()只能建议JVM需要在内存中对垃圾对象进行回收,但不是必须马上回收,

一个是并不能解决内存资源耗空的局面,另外也会增加GC的消耗。

二、JVM内存区域组成

简单的说java中的堆和栈

java把内存分两种:一种是栈内存,另一种是堆内存

1。在函数中定义的基本类型变量和对象的引用变量都在函数的栈内存中分配;

2。堆内存用来存放由new创建的对象和数组


在函数(代码块)中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量所分配的内存空间;在堆中分配的内存由java虚拟机的自动垃圾回收器来管理

堆的优势是可以动态分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的。缺点就是要在运行时动态分配内存,存取速度较慢;



栈的优势是存取速度比堆要快,缺点是存在栈中的数据大小与生存期必须是确定的无灵活性。

java堆分为三个区:New、Old和Permanent



GC有两个线程:

新创建的对象被分配到New区,当该区被填满时会被GC辅助线程移到Old区,当Old区也填满了会触发GC主线程遍历堆内存里的所有对象。Old区的大小等于Xmx减去-Xmn



java栈存放


栈调整:参数有+UseDefaultStackSize -Xss256K,表示每个线程可申请256k的栈空间

每个线程都有他自己的Stack



三、JVM如何设置虚拟内存


提示:在JVM中如果98%的时间是用于GC且可用的Heap size 不足2%的时候将抛出此异常信息。

提示:Heap Size 最大不要超过可用物理内存的80%,一般的要将-Xms和-Xmx选项设置为相同,而-Xmn为1/4的-Xmx值。

提示:JVM初始分配的内存由-Xms指定,默认是物理内存的1/64;JVM最大分配的内存由-Xmx指定,默认是物理内存的1/4。



默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。因此服务器一般设置-Xms、-Xmx相等以避免在每次GC 后调整堆的大小。



提示:假设物理内存无限大的话,JVM内存的最大值跟操作系统有很大的关系。

简单的说就32位处理器虽然可控内存空间有4GB,但是具体的操作系统会给一个限制,

这个限制一般是2GB-3GB(一般来说Windows 系统下为1.5G-2G,Linux 系统下为2G-3G),而64bit以上的处理器就不会有限制了



提示:注意:如果Xms超过了Xmx值,或者堆最大值和非堆最大值的总和超过了物理内存或者操作系统的最大限制都会引起服务器启动不起来。



提示:设置NewSize、MaxNewSize相等,"new"的大小最好不要大于"old"的一半,原因是old区如果不够大会频繁的触发"主" GC ,大大降低了性能



JVM使用-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64;

由XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4。

解决方法:手动设置Heap size

修改TOMCAT_HOME/bin/catalina.bat

在“echo "Using CATALINA_BASE: $CATALINA_BASE"”上面加入以下行:

JAVA_OPTS="-server -Xms800m -Xmx800m -XX:MaxNewSize=256m"



四、性能检查工具使用


定位内存泄漏:

JProfiler工具主要用于检查和跟踪系统(限于Java开发的)的性能。JProfiler可以通过时时的监控系统的内存使用情况,随时监视垃圾回收,线程运行状况等手段,从而很好的监视JVM运行情况及其性能。



1. 应用服务器内存长期不合理占用,内存经常处于高位占用,很难回收到低位;



2. 应用服务器极为不稳定,几乎每两天重新启动一次,有时甚至每天重新启动一次;



3. 应用服务器经常做Full GC(Garbage Collection),而且时间很长,大约需要30-40秒,应用服务器在做Full GC的时候是不响应客户的交易请求的,非常影响系统性能。



因为开发环境和产品环境会有不同,导致该问题发生有时会在产品环境中发生,通常可以使用工具跟踪系统的内存使用情况,在有些个别情况下或许某个时刻确实是使用了大量内存导致out of memory,这时应继续跟踪看接下来是否会有下降,

如果一直居高不下这肯定就因为程序的原因导致内存泄漏。



五、不健壮代码的特征及解决办法


1、尽早释放无用对象的引用。好的办法是使用临时变量的时候,让引用变量在退出活动域后,自动设置为null,暗示垃圾收集器来收集该对象,防止发生内存泄露。

对于仍然有指针指向的实例,jvm就不会回收该资源,因为垃圾回收会将值为null的对象作为垃圾,提高GC回收机制效率;



2、我们的程序里不可避免大量使用字符串处理,避免使用String,应大量使用StringBuffer,每一个String对象都得独立占用内存一块区域;

String str = "aaa";

String str2 = "bbb";

String str3 = str + str2;//假如执行此次之后str ,str2以后再不被调用,那它就会被放在内存中等待Java的gc去回收,程序内过多的出现这样的情况就会报上面的那个错误,建议在使用字符串时能使用 StringBuffer就不要用String,这样可以省不少开销;



3、尽量少用静态变量,因为静态变量是全局的,GC不会回收的;



4、避免集中创建对象尤其是大对象,JVM会突然需要大量内存,这时必然会触发GC优化系统内存环境;显示的声明数组空间,而且申请数量还极大。

这是一个案例想定供大家警戒

使用jspsmartUpload作文件上传,运行过程中经常出现java.outofMemoryError的错误,

检查之后发现问题:组件里的代码

m_totalBytes = m_request.getContentLength();

m_binArray = new byte[m_totalBytes];

问题原因是totalBytes这个变量得到的数极大,导致该数组分配了很多内存空间,而且该数组不能及时释放。解决办法只能换一种更合适的办 法,至少是不会引发outofMemoryError的方式解决。参考:http://bbs.xml.org.cn/blog /more.asp?name=hongrui&id=3747



5、尽量运用对象池技术以提高系统性能;生命周期长的对象拥有生命周期短的对象时容易引发内存泄漏,例如大集合对象拥有大数据量的业务对象的时候,可以考虑分块进行处理,然后解决一块释放一块的策略。



6、不要在经常调用的方法中创建对象,尤其是忌讳在循环中创建对象。可以适当的使用hashtable,vector 创建一组对象容器,然后从容器中去取那些对象,而不用每次new之后又丢弃



7、一般都是发生在开启大型文件或跟数据库 一次拿了太多的数据,造成 Out Of Memory Error 的状况,这时就大概要计算一下数据量的最大值是多少,并且设定所需最小及最大的内存空间值。

分享到:
评论

相关推荐

    Java内存溢出的详细解决方案

    垃圾回收机制可以分为两种情况: * 当应用程序线程空闲时,JVM 会调用垃圾回收机制。 * 当 Java 内存堆不足时,JVM 也会调用垃圾回收机制。 最佳实践 * 设置合适的堆大小和PermGen space大小。 * 降低对象创建...

    详解Java内存溢出的几种情况

    堆内存溢出可以分为两种情况:堆内存溢出和堆内存泄露。堆内存溢出是指堆中对象实例所占的内存空间超出了堆内存的最大容量,而堆内存泄露是指堆中一些对象不再被引用但垃圾回收器无法识别时,这些未使用的对象就会在...

    关于(java heap space)内存溢出的解决办法

    在Java编程中,"java heap space"内存溢出是一个常见的问题,它通常发生在应用程序尝试分配超过JVM堆内存限制的对象时。这个问题对于任何Java开发者来说都至关重要,因为如果不妥善处理,可能会导致程序崩溃。以下是...

    JAVA内存区域与内存溢出异常归类.pdf

    Java内存区域是Java虚拟机(JVM)管理内存的核心组成部分,它们主要分为以下几个部分: 1. **程序计数器(ProgramCounterRegister)**:每个线程都有自己的程序计数器,用于存储当前线程正在执行的字节码的行号指示...

    java内存泄露、溢出检查方法和工具.pdf

    本文主要探讨了Java内存溢出的检查方法和工具,以及内存泄露的基本原理。 首先,当Java应用程序抛出`java.lang.OutOfMemoryError`异常,通常意味着JVM的内存使用超过了预设的限制。JVM内存分为几个区域,包括新生代...

    Java内存溢出和内存泄露

    Java内存溢出和内存泄露 Java内存溢出和内存泄露是Java开发中常见的问题,它们之间存在着紧密的联系,了解它们对于编写高质量的Java代码非常重要。 一、为什么要了解内存泄露和内存溢出? 了解内存泄露和内存溢出...

    java内存泄露、溢出检查方法和工具归纳.pdf

    解决内存泄露的方法主要有两种:一是代码审查,由经验丰富的开发者检查代码以定位问题;二是使用内存分析工具。例如,可以启用JDK的GC日志输出,通过`-verbose:gc`和`-XX:+PrintGCDetails`参数来观察GC的行为,找出...

    Java内存区域与内存溢出异常详解

    垃圾回收机制可以分为两种:Minor GC 和 Full GC。Minor GC 是一种 частичная垃圾回收,主要是清理young generation 区域中的垃圾对象。Full GC 是一种完整的垃圾回收,会清理整个堆中的垃圾对象。 在编写...

    浅谈java内存管理与内存溢出异常

    本文将深入探讨Java内存管理的原理以及内存溢出异常的原因和解决策略。 首先,Java与C/C++在内存管理上的一个重要区别在于自动化程度。在C和C++中,程序员需要手动分配和释放内存,这可能导致内存泄漏和内存溢出...

    JVM原理及内存溢出案列分析PPT教案学习.pptx

    * java中的内存分配分为两个部分:数据堆(Data Heap)和栈(Stack)。程序在运行时一般分配数据堆,把局部的临时变量都放进去,生命周期和进程有关系。但是,如果程序员声明了static变量,就直接在栈中运行的,进程...

    Tomcat内存溢出分析及解决方法

    GC通常在以下两种情况触发:一是当所有线程都处于等待状态,二是当堆内存不足时。过度的GC活动可能导致性能降低,甚至引发内存溢出。因此,优化对象生命周期和减少垃圾对象的生成是防止内存溢出的关键。 为了有效...

    java内存分配之常量池,栈,堆1

    Java内存管理是编程过程中的重要概念,涉及到程序的性能和稳定性。在Java中,内存主要分为四个区域:寄存器、栈、堆和方法区(包括常量池)。以下是这四个区域的详细说明: 1. **寄存器**: 这是计算机硬件的一部分...

    深入理解Java内存模型.7z

    10. **内存溢出问题**:堆内存溢出(Out of Memory Error)通常是由于大量对象创建且没有及时回收导致的。通过分析内存使用情况、合理设计数据结构、优化对象生命周期管理,可以有效预防此类问题。 深入学习Java...

    java 虚拟机参数配置说明及Myeclipse内存不足

    Java程序在启动时可以设置一系列参数,这些参数分为两种类型:标准(也称为官方)参数以“-X”或“-XX:”开头,非标准参数以“-D”开头。标准参数通常涉及JVM的内存分配、垃圾回收机制等;非标准参数则涉及到JVM的...

    JVM内存模型

    在实际应用中,需要根据具体情况选择合适的优化方法,避免内存溢出异常的发生。同时,需要注意JVM所占的内存总量是堆内存、永久区内存和所有线程所占的栈内存总和。 JVM内存模型是Java虚拟机(JVM)中的一种内存...

    模拟内存申请与释放

    内存分为两种主要类型:栈内存和堆内存。栈内存主要存放基本类型的变量和对象的引用,它的生命周期与所在的方法同步,方法结束时自动回收。而堆内存则用于存储对象实例,其生命周期由引用决定,当没有引用指向一个...

    java OOM内存泄漏原因及解决方法

    3. java.lang.StackOverflowError ----> 不会抛OOM error,但也是比较常见的Java内存溢出。 这种情况一般是由于程序中存在死循环或者深度递归调用造成的,栈大小设置太小也会出现此种溢出。可以通过虚拟机参数-Xss...

    缓冲区溢出介绍.pdf

    缓冲区溢出可以分为两种类型:栈溢出和堆溢出。栈溢出是指缓冲区溢出到栈中,可能会导致程序崩溃或安全漏洞。堆溢出是指缓冲区溢出到堆中,可能会导致程序崩溃或内存泄露。 缓冲区溢出可以通过多种方式来防止,例如...

    OOMDemo,.zip

    《深入理解Java内存溢出与OOMDemo开源项目分析》 在Java编程中,内存管理是开发者必须关注的重要一环。当程序运行时,如果分配给Java虚拟机(JVM)的内存不足,就会出现“OutOfMemoryError”(简称OOM),这是一种...

    图片压缩类

    图片压缩分为无损压缩和有损压缩两种。无损压缩方法可以完全恢复原始数据,如PNG、GIF格式,它们通过寻找和消除冗余数据来压缩。有损压缩则会牺牲部分质量来换取更高的压缩比,如JPEG格式,它利用人类视觉系统的...

Global site tag (gtag.js) - Google Analytics