浅谈Java--内存泄漏

标签: java 内存泄漏 | 发表时间:2013-01-23 00:35 | 作者:
出处:http://www.iteye.com
      JAVA的垃圾回收机制,让许多程序员觉得内存管理不是很重要,但是内存内存泄露的事情恰恰这样的疏忽而发生,特别是对于Android开发,内存管理更为重要,养成良好的习惯,有利于避免内存的泄漏.
 
对象的几种状态:
    这里可以把许多对象和引用看成是有向图,顶点可以是对象也可以是引用,引用关系就是有向边。
  1. 可达状态:对象创建的时候,有引用指向它,这个时候在对象和引用之间建立了引用关系,即由引用发射有向边指向对象,这个对象就是出于可达状态
  2. 可恢复状态:当引用不指向一个对象的时候,该对象就处于可恢复状态,这时候在系统回收该对象之前,会调用finalize方法进行资源清理,如果调用这个方法后能重新让引用变量去引用他,那么他又恢复到可达状态,不然会变成不可达状态。
  3. 不可达状态:当对象和引用变量失去了引用关系,并且调用了finalize方法后,不能恢复到可达状态,那么将永久性失去引用,此时系统才会真正去回收对象占用的内存。
 
 
何为内存泄漏:
    内存泄漏就是程序运行中,java垃圾回收机制,会对一些不适用的内存进行回收,然后再重新分配,保证系统能再次用到这些内存,Java中所有不可达状态都会被回收,但是一些处于可达状态,但是程序却再也不会用到的对象(占着茅坑不拉屎),这些内存就无法被回收,他们对于程序员来说已经没用,但是对于垃圾回收机制来说,他们是可达的,所以是“有用的”这些占用的内存就会出现内存泄漏。
 
 用一个栈的例子说明:
<!--WizRtf2Html Charset=0 -->
<!--WizRtf2Html Charset=0 -->
package CrazyJava;
import javax.management.RuntimeErrorException;
/**
 * 
 * @author ccf
 *
 */
public class neicun {
    /**
     * @param args
     */
     class Stack {
         private Object[] elementData;
         private int size;
         private int capacityIncrement;
         public Stack( int initialCapacity) {
            elementData = new Object[initialCapacity];
        }
         public Stack( int initialCapacity, int capacityIncrement) {
            elementData = new Object[initialCapacity];
             this.capacityIncrement = capacityIncrement;
        }
         public void push(Object object) {
            ensureCapacity();
            elementData[size++] = object;// 后加
        }
         public Object pop() {
             if (size == 0)
                 throw new RuntimeException("空栈异常");
            Object ele = elementData[--size];// 这里为局部变量,当方法结束,局部变量会被回收
            elementData[size] = null;// 消去强引用关系,避免产生内泄漏。
             return ele;// 返回栈顶元素 size自减1个长度
        }
         public int size() {
             return size;
        }
         private void ensureCapacity() {
            // TODO Auto-generated method stub
            // 数组已经满了。进行扩容。
             if (elementData.length == size) {
                Object[] oldElmentata = elementData;
                 int newLength = 0;
                 if (capacityIncrement > 0) {
                    newLength = elementData.length + capacityIncrement;
                } else {
                    newLength = ( int) (elementData.length * 1.5);
                }
                elementData = new Object[newLength];
                System.arraycopy(oldElmentata, 0, elementData, 0, size);
            }
        }
    }
     public static void main(String[] args) {
        // TODO Auto-generated method stub
        Stack stack = new neicun(). new Stack(10);
         for ( int i = 0; i < 10; i++) {
            stack.push("元素" + i);
        }
         for ( int i = 0; i < 10; i++) {
            System.out.println(stack.pop());
        }
    }
}
 
 
代码中pop() 函数实现出栈,其中 elementData[size] = null;切断了元素和其引用的引用关系,这样可以使元素进入不可达状态,从能被系统回收,如果不切断引用,该元素将一直可以可达状态,就会常驻内存。
而针对上一句Object ele = elementData[--size];  就是为了暂存这个元素,用于作为return的返回值,有人会问这个为什么不会造成内存泄露,他们之间不是已经有了引用和被引用的关系,原因是这里的Object对象是局部变量,局部变量的生命周期跟方法生命周期一样,该方法结束了,搞局部变量会被垃圾回收机制收回,所以这个担心是没必要的。
 
 
        从以上看来,内存泄漏可能性还是很大的,强引用类型(以后的博文会讲)使我们用得最多的类型,这类引用只有再失去引用的时候才会被回收,其他情况都是不能被回收的,所以良好的编程习惯是避免内存泄漏的好办法
 
  1. 尽量多使用直接量    
    例如String类型 
    String a =“ccf”  //采用直接量,JVM字符串池会缓存这个字符串
    String b = new String(“ccf”); //但是直接调用构造方法的话,因为String内部是基于数组,所以会产生 字符数组存储ccf三个字符。
  2. 多使用StringBuilder和StringBuffer
    使用StringBuilder和StringBuffer进行字符串的操作,可以减少使用String进行字符串操作产生的临时字符串
  3. 释放无用的对象引用,就像上面栈的 pop ()方法
  4. 少用静态变量,静态变量生命周期跟类一样,为类加载到类卸载这段时间,也就是知道程序结束。
  5. 避免在循环和经常调用的方法创建对象,因为这些情况会产生大量对象,特别是像for循环这些。
  6. 缓存经常用到的对象,避免重复去创建相同对象,想android中Adapter中重写getView就经常用到对象缓存技术。


已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [java 内存泄漏] 推荐:

java内存泄漏

- - 编程语言 - ITeye博客
不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址. Java中对象是采用new或者反射的方法创建的,这些对象的创建都是在堆(Heap)中分配的,所有对象的回收都是由Java虚拟机通过垃圾回收机制完成的. GC为了能够正确释放对象,会监控每个对象的运行状况,对他们的申请、引用、被引用、赋值等状况进行监控,Java会使用有向图的方法进行管理内存,实时监控对象是否可以达到,如果不可到达,则就将其回收,这样也可以消除引用循环的问题.

浅谈Java--内存泄漏

- - ITeye博客
      JAVA的垃圾回收机制,让许多程序员觉得内存管理不是很重要,但是内存内存泄露的事情恰恰这样的疏忽而发生,特别是对于Android开发,内存管理更为重要,养成良好的习惯,有利于避免内存的泄漏..     这里可以把许多对象和引用看成是有向图,顶点可以是对象也可以是引用,引用关系就是有向边.

(转)Java中字符串与内存泄漏的问题

- - jackyrong
对于这个写法,实际上对于oldStr是一个char[]数组[h,e,l,l,0,,,c,l,a,r,k],对于subString操作,newStr并不是自己copy oldStr的char[]数组hello自己去创建一个新的char[]数组,而是java在背后进行了String Reusing Optimization,它不会自己创建一个新的char数组,而是reuse原来的char数组.

JAVA内存泄漏问题处理方法经验总结

- - 编程语言 - ITeye博客
JVM问题,一般会有三种情况,目前遇到了两种,线程溢出和JVM不够用. 1.线程溢出:unable to create new native thread. 系统在1月4号左右,突然发现会产生内存溢出问题,从日志上看,错误信息为:. 导致系统不能使用,对外不能相应,但是观察gc等又处于正常情况,free 系统内存也正常.

内存泄漏

- - CSDN博客系统运维推荐文章
程序申请了堆空间,但是“忘记”释放,导致该块区域在程序结束前无法被再次使用导致的. 泄漏时间长了,就会导致用户空间内存不足,严重的导致死机. 如果泄漏比较严重,很容易察觉;但是有些泄漏很缓慢,不容易察觉,但是软件会运行很长时间后,会慢慢导致严重问题,而且当发现症状的时候,基本上已经是比较晚的时候了,想要识别泄漏,还是可以实现的,本篇文章来聊聊内存操作的原理.

面试官:小伙子,你给我说一下Java中什么情况会导致内存泄漏呢?

- - SegmentFault 最新的文章
内存泄露:指程序中动态分配内存给一些临时对象,但对象不会被GC回收,它始终占用内存,被分配的对象可达但已无用. 即无用对象持续占有内存或无用对象的内存得不到及时释放,从而造成的内存空间浪费. JVM使用可达性分析算法判断对象是否存活. 通过一系列名为“GC Roots”的对象作为起点,从这些结点开始向下搜索,搜索所走过的路径称为“引用链(Reference Chain)”,当一个对象到GC Roots没有任何饮用链相连时,则证明此对象是不可用的.

Android 解析内存泄漏

- - CSDN博客移动开发推荐文章
1、引用没释放造成的内存泄露.        1.1、注册没取消造成的内存泄露.        这种 Android的内存泄露比纯 Java的内存泄露还要严重,因为其他一些Android程序可能引用我们的Anroid程序的对象(比如注册机制). 即使我们的Android程序已经结束了,但是别的引用程序仍然还有对我们的Android程序的某个对象的引用,泄露的内存依然不能被垃圾回收.

Perfdog玩转内存泄漏

- - 操作系统 - ITeye博客
最近QC同学在跑游戏的过程中发现玩的时间久了游戏会发生闪退,经过搜集信息后排除了功能性bug的. 拿到真机,USB连接,杀掉多余后台进程,打开Perfdog,接下来一顿操作猛如虎,Perfdog具体操作不在赘述,有关perfdog怎么使用的教程可以参考. 此图一出,基本就可以断定内存泄露了,这是正常玩游戏,游戏运行了30分钟的内存趋势图;.

shared_ptr真能防止内存泄漏吗?

- Roger - codedump
这个命题有些诡异,因为shared_ptr设计的初衷就是为了防止内存泄漏,但是先别急,等我把问题描述清楚.. 事出缘由是这几天项目出现一个内存泄漏的bug,之前这部分是使用shared_ptr封装了很多指针的操作,后来出于效率的考虑,改回了裸指针.由于我们使用的google tcmalloc做内存分配,它自带了检测内存泄漏的功能,于是在单元测试的时候就被检查出了内存泄漏..

(转)ThreadLocal的内存泄漏问题

- - 编程语言 - ITeye博客
原文:http://www.godiscoder.com/?p=479. 在最近一个项目中,在项目发布之后,发现系统中有内存泄漏问题. 表象是堆内存随着系统的运行时间缓慢增长,一直没有办法通过gc来回收,最终于导致堆内存耗尽,内存溢出. 开始是怀疑ThreadLocal的问题,因为在项目中,大量使用了线程的ThreadLocal保存线程上下文信息,在正常情况下,在线程开始的时候设置线程变量,在线程结束的时候,需要清除线程上下文信息,如果线程变量没有清除,会导致线程中保存的对象无法释放.