Effective.Java 读书笔记(6)内存泄漏

6.Eliminate obsolete object reference

大意为 消除旧的对象引用

当你使用直接操作内存的语言,例如C或者C++的时候,一些内存释放的操作会比较麻烦,而我们使用java这一种拥有垃圾回收机制的语言的时候,这份工作就变得轻松多了,但是要注意的是,这个垃圾回收机制并不能让我们对于内存管理掉以轻心

考虑一下下面这个栈类型的实现

// Can you spot the "memory leak"?
public class Stack {
  private Object[] elements;
  private int size = 0;
  private static final int DEFAULT_INITIAL_CAPACITY = 16;

  public Stack() {
       elements = new Object[DEFAULT_INITIAL_CAPACITY];
  }

  public void push(Object e) {
    ensureCapacity();
    elements[size++] = e;
  }

  public Object pop() {
    if (size == 0)
         throw new EmptyStackException();
    return elements[--size];
  }

/**
* Ensure space for at least one more element, roughly
* doubling the capacity each time the array needs to grow.
*/
  private void ensureCapacity() {
    if (elements.length == size)
         elements = Arrays.copyOf(elements, 2 * size + 1);
    }
}

看上去好像没有什么明显的问题,测试起来也都可以通过对吧,但是注意到其中潜伏的一个问题,宽泛点说,这个程序有着内存泄漏的风险,这样的风险会导致垃圾回收的压力增大并且加大内存的开销从而降低整个程序性能,最严重的时候可能会产生OutOfMemoryError的错误,但是这样的错误比较少见

那么是那一部分内存泄漏呢,其实就是pop弹出栈操作中stack仍然保留着已经弹出的element的引用,那样垃圾回收机制并不会去回收,并且这样的一个旧的引用并不会被重引用,即使我们的stack没有所有element的引用了,垃圾回收机制也不会去回收,由于stack一直维持着一个旧的引用

内存泄漏在拥有垃圾回收机制(更加适合的说法是,无意的对象保留)的语言里面是十分阴险的,如果一个对象的引用无意间保留了下来,不仅仅这个对象不会被垃圾回收,那些被这个对象所引用的对象也不能被回收,链式效应会使得整个程序的性能极具下降

为了解决这样的一个问题,我们只需要简单地把那些引用置为null就可以了,比如在上述程序中,我们只要这样修改就好

public Object pop() {
  if (size == 0)
       throw new EmptyStackException();
  Object result = elements[--size];
  elements[size] = null; // Eliminate obsolete reference
  return result;
}

只要你重引用这个元素,系统就会抛出一个空指针异常,这对于检测程序异常错误十分常见

当程序员第一次面临这个问题的时候,他们可能会在程序结束使用的时候过度去置空每一个对象的引用,这样是既没有必要又不被期望的,这样会使得代码变得杂乱,置空对象的引用应该视情况而定而不是规范式地照搬,关于消除旧引用的最好的办法就是让这些包含引用的变量越界,这经常在你在一个狭窄的范围内定义一个变量的时候会发生

那么,什么时候我们去置空这些引用呢?简单来说,当你的类中所拥有的变量的引用没有重用的可能并且你的类还继续拥有着这个引用的话,就把它置为空就可以了,如果不置为空,垃圾回收机制并不知道这个引用没有作用了,也就不会去回收了

总而言之,只要当一个类管理它自己的内存,程序员就应该注意一下内存泄漏的风险,当一个元素是free的时候,任何对这个元素的引用都应该被置空

另一个比较常见的可能造成内存泄漏的原因就是缓存了,一旦你把一个对象的引用放到缓存里面,很容易忘记它在缓存那里并且很容易就把它一直放在缓存那里知道它变得完全没有作用了,对于这类问题,有着一些解决方案,如果你足够幸运,实现的缓存的条目都是完全相关的并且只要对于键值存在缓存外部的引用,代表性的缓存例如WeakHashMap,一旦条目变过时了就会自动被移除,记住WeakHashMap,这个类很有用当缓存条目的生存时间取决于外部对于键值的引用,而不是值的引用的时候

更加常见的是,一个缓存条目的有用的生存时间很少被定义的很好,随着时间条目变得越来越没有价值,在这种情况下,缓存应该偶尔清一下那些不用了的条目,这可以利用后台线程来处理(可能是一个Timer 或者是一个ScheduledThreadPoolExecutor )或者添加新的条目的时候就会有这种作用,LinkedHashMap类使用了它的removeEldestEntry方法使得后一种方法更加简便,对于更加复杂的缓存,你可能需要直接的使用 java.lang.ref

第三种常见的内存泄漏的就是监听器和其他的回调,如果你实现了一个API,这个API是当用户注册回调但是并没有明确的解除注册,他们会积累起来除非你采取某些措施,最好的办法来保正这些回调被垃圾回收及时处理就是只储存weak reference(弱引用),对于实例,就利用WeakHashMap储存他们作为键

因为内存泄漏不会特别的明显地显示出来,他们可能在某个系统里面潜藏很久,只有十分仔细的代码或者debug工具(比如heap profiler)的辅助才能发现他们,因此,我们需要学习去在这些问题发生之前预测并且防止他们发生

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • Java引用的种类 1.对象在内存中的状态 对于JVM的垃圾回收机制来说,是否回收一个对象的标准在于:是否还有引用...
    Jack921阅读 3,842评论 0 3
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,573评论 18 399
  • Android 内存泄漏总结 内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。内存泄漏...
    _痞子阅读 1,624评论 0 8
  • 最近正在熟悉Java内存泄漏的相关知识,上网查阅了一些资料,在此做个整理算是对收获的一些总结,希望能对各位有所帮助...
    李序锴阅读 28,821评论 4 62
  • 内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。内存泄漏大家都不陌生了,简单粗俗的讲,...
    宇宙只有巴掌大阅读 2,360评论 0 12