`
usenrong
  • 浏览: 517455 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

浅析 内存泄漏 的原因

    博客分类:
  • J2EE
jvm 
阅读更多

内存泄漏原因有很多种,比如:

1.数据量过于庞大;死循环 ;静态变量和静态方法过多;递归;无法确定是否被引用的对象;

2.虚拟机不回收内存(内存泄漏);

    说白了就是程序运行要用到的内存大于虚拟机能提供的最大内存就发生内存溢出了。 内存溢出的问题要看业务和系统大小而定,对于某些系统可能内存溢出不常见,但某些系统还是很常见的解决的方法,

一个是优化程序代码,如果业务庞大,逻辑复杂,尽量减少全局变量的引用,让程序使用完变量的时候释放该引用能够让垃圾回收器回收,释放资源。
二就是物理解决,增大物理内存,然后通过:-Xms256m -Xmx256m -XX:MaxNewSize=256m -XX:MaxPermSize=256m的修改.

 

一、内存溢出类型
1 、 java.lang.OutOfMemoryError: PermGen space

JVM 管理两种类型的内存,堆和非堆。堆是给开发人员用的上面说的就是,是在 JVM 启动时创建;非堆是留给 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 的角色

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””上面加入以下行:

 
  1. 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 对象都得独立占用内存一块区域;

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

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

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

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

使用jspsmartUpload作文件上传,现在运行过程中经常出现java.outofMemoryError的错误,用top命令看看进程使用情况,发现内存不足2M,花了很长时间,发现是jspsmartupload的问题。把jspsmartupload组件的源码文件(class文件)反编译成Java文件,如梦方醒:

 
  1. m_totalBytes = m_request.getContentLength();        
  2. m_binArray = new byte[m_totalBytes];      

变量m_totalBytes表示用户上传的文件的总长度,这是一个很大的数。如果用这样大的数去声明一个byte数组,并给数组的每个元素分配内存空间,而且m_binArray数组不能马上被释放,JVM的垃圾回收确实有问题,导致的结果就是内存溢出。

jspsmartUpload为什末要这样作,有他的原因,根据RFC1867的http上传标准,得到一个文件流,并不知道文件流的长度。设计者如果想文件的长度,只有操作servletinputstream一次才知道,因为任何流都不知道大小。只有知道文件长度了,才可以限制用户上传文件的长度。为了省去这个麻烦,jspsmartUpload设计者直接在内存中打开文件,判断长度是否符合标准,符合就写到服务器的硬盘。这样产生内存溢出,这只是我的一个猜测而已。

所以编程的时候,不要在内存中申请大的空间,因为web服务器的内存有限,并且尽可能的使用流操作,例如

 
  1. byte[] mFileBody = new byte[512];   
  2.         Blob vField= rs.getBlob("FileBody");    
  3.      InputStream instream=vField.getBinaryStream();   
  4.      FileOutputStream fos=new FileOutputStream(saveFilePath+CFILENAME);   
  5.          int b;   
  6.                       while( (b =instream.read(mFileBody)) != -1){   
  7.                        fos.write(mFileBody,0,b);   
  8.                         }   
  9.        fos.close();   
  10.      instream.close();  

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

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

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

分享到:
评论
1 楼 usenrong 2012-09-04  
jprofiler 7.* license

L-Larry_Lau@163.com#24777-1i8da63tvtyl2#1119
L-Larry_Lau@163.com#61624-1dvrt8wj18v1#6260
L-Larry_Lau@163.com#50028-se4zkrr1m6t1#10246
L-Larry_Lau@163.com#15600-189y158nwwvuk#339
L-Larry_Lau@163.com#30640-1lklqdbcjmhxs#4016
L-Larry_Lau@163.com#57474-53b2wr1311gnz#10228
L-Larry_Lau@163.com#19667-11r2awc10nqelb#4016
L-Larry_Lau@163.com#60353-pphob7wraf0y#515
L-Larry_Lau@163.com#65157-1ae6ytp7ygj8m#0012
L-Larry_Lau@163.com#16226-1n5h5951019s7s#7343

相关推荐

    常见内存泄漏与对策及预防措施浅析

    标题和描述中所提及的知识点主要集中在C/C++编程语言中的内存管理问题,特别是内存泄漏这一常见问题。内存泄漏指的是程序在运行过程中未能正确释放已分配的内存,导致这部分内存无法再次被使用,最终可能耗尽系统...

    C++程序设计内存泄漏浅析

    ### C++程序设计内存泄漏浅析 #### 一、引言 C++作为一种广泛使用的编程语言,因其灵活性和高效性而备受青睐。然而,在实际开发过程中,如果不慎使用其动态内存管理功能,很容易引发内存泄漏问题。内存泄漏不仅会...

    ThreadLocal中内存泄漏和数据丢失问题的问题浅析及解决方案.docx

    Entry 中的 key 在 GC 的时候会被回收,但是对应的 Value 却还存在,这样就会造成 key(null) 的情况,对应的 value 也会取不到,这就是内存泄漏的原因。同时也会造成数据丢失。 解决方案: 为了解决 ThreadLocal ...

    linux 共享内存浅析

    - 在使用共享内存时,需要注意内存管理,避免出现内存泄漏等问题。 - 确保所有使用共享内存的进程都正确地执行了解除挂载和销毁操作,以免造成资源浪费。 通过以上步骤,我们可以有效地在Linux环境下利用共享内存...

    Java内存分配浅析

    理解Java内存分配原理有助于优化程序性能,避免内存泄露等问题。 在JVM中,内存主要划分为以下几个区域: 1. **寄存器**:这是JVM内部的虚拟寄存器,用于存储快速存取的数据,但程序员无法直接控制。 2. **栈...

    Android防止内存溢出浅析.zip

    - **LeakCanary**:自动检测内存泄漏的开源库,简化了内存泄漏的检测和修复过程。 5. **内存优化最佳实践** - **遵循设计模式**:例如使用 MVP 或 MVVM 结构,可以更好地管理对象的生命周期。 - **使用Android ...

    浅析C# 内存管理

    ### 浅析C# 内存管理 #### 内存管理概述 在现代软件开发过程中,内存管理一直是至关重要的组成部分。对于C#这样的高级编程语言而言,它为开发者提供了自动化的内存管理机制,大大减轻了程序员在内存管理方面的负担...

    Java中堆内存与栈内存分配浅析

    理解这两种内存类型的工作原理及其区别对于优化程序性能、避免内存泄漏等问题至关重要。本文将深入探讨Java中堆内存与栈内存的分配机制,并通过对比分析它们之间的差异,帮助读者更好地掌握Java内存管理的核心概念。...

    防止内存溢出浅析

    1. **理解内存泄漏**:内存泄漏是指对象不再使用,但仍然有引用指向它,使得GC无法回收。例如,静态变量、单例模式、匿名内部类、非静态内部类持有外部类引用等都可能导致内存泄漏。因此,开发时应避免不正确的引用...

    Android防止内存溢出浅析

    本文将深入探讨Android平台上的内存管理机制,以及如何避免内存泄露问题。 首先,我们需要理解Android内存管理的基本原理。Android应用程序基于Java语言,因此其内存机制与Java相似。Java的垃圾回收(GC)机制会自动...

    基于Android防止内存溢出浅析

    内存泄漏是导致内存溢出的主要原因之一。当一个对象不再被程序使用但仍然保留在内存中,就发生了内存泄漏。例如,活动(Activity)没有正确释放资源,静态变量持有大量对象引用,或者单例模式中不恰当的生命周期管理...

    Android应用源码之防止内存溢出浅析.zip

    - 异步任务滥用:例如在AsyncTask中,如果任务未正确关闭,可能会导致内存泄漏。 - 无限递归或循环:这会导致CPU持续运行,占用大量内存。 - 未释放的资源:如Bitmap、Cursor等资源,如果没有及时关闭,可能导致...

    应用源码之防止内存溢出浅析.zip

    3. **内存泄漏**:未被正确释放的资源可能导致内存泄漏,例如匿名内部类、静态引用、单例模式等常见场景。分析源码时,要特别关注可能导致长期持有对象的代码结构。 4. **内存溢出**:当应用程序请求的内存超过了...

    C++中常见内存错误及对策浅析

    2. **使用智能指针**:C++11引入了智能指针的概念,如`std::unique_ptr`和`std::shared_ptr`,这些智能指针能够自动管理资源的生命周期,减少了内存泄漏的风险。 3. **代码审查**:定期进行代码审查可以帮助团队...

    安卓Android源码——防止内存溢出浅析.zip

    1. **内存泄漏的常见原因**: - 静态字段:静态字段会使得对象生命周期与应用的生命周期一致,除非显式设为null,否则容易造成内存泄漏。 - 单例模式:如果单例中持有Activity引用,可能导致Activity无法被GC回收...

    浅析Node.js中的内存泄漏问题

    Node.js是一款允许JavaScript在服务器端运行的框架,近年来由于其...通过了解内存泄漏的原因和种类,选择合适的工具进行分析和诊断,并结合良好的编程实践,开发者可以有效地控制和解决Node.js应用中的内存泄漏问题。

Global site tag (gtag.js) - Google Analytics