Java 虚拟机内存溢出问题和解决方法

一什么是内存溢出

1内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能提供的最大内存。
2 Java的内存管理就是对象的分配和释放问题。
在Java中,内存的分配是由程序完成的,而内存的释放是由垃圾收集器(Garbage Collection,GC)完成的,程序员不需要通过调用GC函数来释放内存,因为不同的JVM实现者可能使用不同的算法管理GC,有的是内存使用到达一定程度时,GC才开始工作,也有定时执行的,有的是中断式执行GC。但GC只能回收无用并且不再被其它对象引用的那些对象所占用的空间。Java的内存垃圾回收机制是从程序的主要运行对象开始检查引用链,当遍历一遍后发现没有被引用的孤立对象就作为垃圾回收。

二内存溢出的原因:

1内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
2集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
3代码中存在死循环或循环产生过多重复的对象实体;
4使用的第三方软件中的BUG;
5启动参数内存值设定的过小;

三解决办法

1修改JVM启动参数,直接增加内存。
JVM默认可以使用的内存为64M,Tomcat默认可以使用的内存为128MB。这对于其他大型引用绝对是不够的
2检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。

3对代码进行走查和分析,
检查代码中是否有死循环或递归调用。
检查是否有大循环重复产生新对象实体。
检查对数据库查询中,是否有一次获得全部数据的查询,而没有使用分页。
检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。
4、尽早释放无用对象的引用。好的办法是使用临时变量的时候,让引用变量在退出活动域后,自动设置为 null ,暗示垃圾收集器来收集该对象,防止发生内存泄露。
对于仍然有指针指向的实例, jvm 就不会回收该资源 , 因为垃圾回收会将值为 null 的对象作为垃圾,提高 GC 回收机制效率;
5 、我们的程序里不可避免大量使用字符串处理,避免使用 String ,应大量使用 StringBuffer ,每一个 String 对象都得独立占用内存一块区域;
String str = “aaa”;

String str2 = “bbb”;

String str3 = str + str2;// 假如执行此次之后 str ,str2 以后再不被调用 , 那它就会被放在内存中等待 Java 的 gc 去回收 , 程序内过多的出现这样的情况就会报上面的那个错误 ,

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

7 、避免集中创建对象尤其是大对象, JVM 会突然需要大量内存,这时必然会触发 GC 优化系统内存环境;显示的声明数组空间,而且申请数量还极大。
比如:
有个excel文件上传的功能,excel内容有非常大,每次上传都导致jvm需要大量的内存。

8 、尽量运用对象池技术以提高系统性能;生命周期长的对象拥有生命周期短的对象时容易引发内存泄漏,例如大集合对象拥有大数据量的业务对象的时候,可以考虑分块进行处理,然后解决一块释放一块的策略。
9 、不要在经常调用的方法中创建对象,尤其是忌讳在循环中创建对象。可以适当的使用 hashtable , vector 创建一组对象容器,然后从容器中去取那些对象,而不用每次 new 之后又丢弃
10 、一般都是发生在开启大型文件或跟数据库一次拿了太多的数据,造成 Out Of Memory Error的状况,这时就大概要计算一下数据量的最大值是多少,并且设定所需最小及最大的内存空间值。

四使用内存查看工具动态查看内存使用情况

1内存查看工具有许多,比较有名的有:Optimizeit Profiler、JProbe Profiler、JinSight和Java1.5的Jconsole等。它们的基本工作原理大同小异,都是监测Java程序运行时所有对象的申请、释放等动作,将内存管理的所有信息进行统计、分析、可视化。开发人员可以根据这些信息判断程序是否有内存泄漏问题。一般来说,一个正常的系统在其启动完成后其内存的占用量是基本稳定的,而不应该是无限制的增长的。持续地观察系统运行时使用的内存的大小,可以看到在内存使用监控窗口中是基本规则的锯齿形的图线,如果内存的大小持续地增长,则说明系统存在内存泄漏问题。通过间隔一段时间取一次内存快照,然后对内存快照中对象的使用与引用等信息进行比对与分析,可以找出是哪个类的对象在泄漏。

五内存溢出类型

1 java.lang.OutOfMemoryError: PermGen space
1JVM 管理两种类型的内存,堆和非堆。
2堆是给开发人员用的 ;
3非堆是留给 JVM 自己用的,用来存放类的信息的,运行期内 GC 不会释放空间。
如果 web app 用了大量的第三方 jar 或者应用有太多的 class 文件而恰好 MaxPermSize 设置较小,超出了也会导致这块内存的占用过多造成溢出,或者 tomcat 热部署时侯不会清理前面加载的环境,只会将 context 更改为新部署的,非堆存的内容就会越来越多。

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 的角色

1当应用程序线程空闲,调用 GC。
2java 内存堆不足时,会不断调用 GC ,若连续回收都解决不了内存堆不足的问题时,就会报 out of memory
3 根据 GC 的机制,程序的运行会引起系统运行环境的变化,增加 GC 的触发机会。
建议:
程序的设计和编写应避免垃圾对象的内存占用和 GC 的开销。显示调用 System.GC() 只能建议 JVM 需要在内存中对垃圾对象进行回收,但不是必须马上回收,
并不能解决内存资源耗空的局面,另外也会增加 GC 的消耗。

七 JVM 内存区域组成

java把内存分两种:一种是栈内存,另一种是堆内存
1栈内存:
存放函数中定义的基本类型变量和对象的引用变量
2堆内存:
存放由 new创建的对象和数组

在函数(代码块)中定义一个变量时, java就在栈中为这个变量分配内存空间,当超过变量的作用域后, java会自动释放掉为该变量所分配的内存空间;在堆中分配的内存由 java虚拟机的自动垃圾回收器来管理
优缺点
堆的优势是可以动态分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的。缺点就是要在运行时动态分配内存,存取速度较慢;
栈的优势是存取速度比堆要快,缺点是存在栈中的数据大小与生存期必须是确定的无灵活 性。
java 堆分为三个区: New 、 Old 和 Permanent
GC 有两个线程:
新创建的对象被分配到 New 区,当该区被填满时会被 GC 辅助线程移到 Old 区,当 Old 区也填满了会触发 GC 主线程遍历堆内存里的所有对象。 Old 区的大小等于 Xmx 减去 -Xmn
栈调整:参数有 +UseDefaultStackSize -Xss256K,表示每个线程可申请 256k的栈空间
每个线程都有他自己的 Stack

八、 JVM如何设置虚拟内存

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

1.提示: Heap Size 最大不要超过可用物理内存的 80%,一般的要将 -Xms和 -Xmx选项设置为相同,而 -Xmn为 1/4的 -Xmx值。
2.提示: JVM初始分配的内存由 -Xms指定,默认是物理内存的 1/64; JVM最大分配的内存由 -Xmx指定,默认是物理内存的 1/4。
3默认空余堆内存小于 40%时, JVM就会增大堆直到 -Xmx的最大限制;空余堆内存大于 70%时, JVM会减少堆直到 -Xms的最小限制。因此服务器一般设置 -Xms、 -Xmx相等以避免在每次 GC 后调整堆的大小。
4.提示:假设物理内存无限大的话, JVM内存的最大值跟操作系统有很大的关系。
简单的说就 32位处理器虽然可控内存空间有 4GB,但是具体的操作系统会给一个限制,
这个限制一般是 2GB-3GB(一般来说 Windows系统下为 1.5G-2G, Linux系统下为 2G-3G), 而 64bit以上的处理器就不会有限制了
5.提示:注意:如果 Xms超过了 Xmx值,或者堆最大值和非堆最大值的总和超过了物理内 存或者操作系统的最大限制都会引起服务器启动不起来。
6.提示:设置 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”

九内存溢出现象

  1. 应用服务器内存长期不合理占用,内存经常处于高位占用,很难回收到低位;
  2. 应用服务器极为不稳定,几乎每两天重新启动一次,有时甚至每天重新启动一次;
  3. 应用服务器经常做 Full GC(Garbage Collection),而且时间很长,大约需要 30-40秒,应用服务器在做 Full GC的时候是不响应客户的交易请求的,非常影响系统性能。

猜你喜欢

转载自blog.csdn.net/zhou920786312/article/details/81129528
今日推荐