第6条 消除过期的对象引用

图1

如图所示的例子,这段程序中没有明显的错误,但是存在一个隐藏的问题(“内存泄漏”),随着垃圾回收活动的增加,或者由于内存占用的不断增加,程序性能的降低会逐渐表现出来,在极端的情况下,这种内存泄漏会导致磁盘交换,甚至导致程序失败。

如果栈先是增长,然后在收缩,那么从栈中弹出的对象不会被当做垃圾回收,即使使用栈的程序不在引用这些对象,他们也不会被回收。因为,栈内部维护着对这些对象的过期引用。过期引用是指永远也不会解除的引用。在以上这个例子中,elements数组的活动部分之外的所有引用都是过期的。活动部分是指elements数组中下标小于size的部分。

如果一个对象引用被无意识的保留下来,那么垃圾回收机制不仅不会处理这个对象,而且也不会处理处理这个对象所引用的所有其他对象。即使少量的几个对象被无意识的保留下来,也许会有许多对象被排除在垃圾回收之外,并由此对性能造成重大影响。

解决方法:一旦引用对象已经过期,清空这些引用。

例子中的statck类,只要一个单元被弹出,指向他的引用就过期了,pop方法修改为

public object pop(){

if(size ==0){

throw new EmptyStackException();

}

ObJect result = elements[--size]//--放在前面为先减一再使用

element[size] = null;

return result;

}

清空过期引用的另外一个好处是,如果他们以后又被错误的解除引用,程序会立即抛出空指针异常,而不是悄悄的错误的运行下去。

清空对象引用应该是一种例外,而不是一种规范行为。消除过期引用的最好的方法是让包含该引用的变量结束其生命周期。如果在最紧凑的作用域范围内定义一个变量

如下例 1

Iterator iter = l.iterator();

while(iter.hasNext()){

String str = (String) iter.next();

System.out.println(str);

}

例2

while(Iterator iter = l.iterator();iter.hasNext()){

String str = (String) iter.next();

System.out.println(str);

}

例2这种就比较合适,iter的生命周期循环结束后结束

stack类自己管理内存,存储池包含了elements数组的元素,数组活动区域中的元素是已分配的,而数组其余部分的元素是自由的,但是垃圾回收器并不知道这一点,对于垃圾回收来说,elements数组中的所有对象引用都同等有效。只有我们本身知道数组的非活动部分是不重要的,所以需要我们手动清空这些元素。

一般而言,只要是自己管理内存,就应该警惕内存泄漏问题。一旦元素被释放掉,则该元素中包含的任何对象引用都应被清空掉。


内存泄漏的另一个常见的来源是缓存,一旦把对象引用放入缓存中,它就很容易被遗忘。对于这种情况有几种可能的解决方案,如果你正好要实现一个只要在缓存之外存在对某个项的键的引用,该项就有意义这样的缓存的话,就可以使用WeakHashMap代表缓存,因为当缓存中的项过期的时候,它们就会自动被删除掉。但是只有当所要的缓存项的生命周期是由key的外部引用而不是由value决定时WeakHashMap才有用处。

更常见的情况是“缓存项的生命周期是否有意义”,并不是很容易确定,随着时间的推移,其中的项会变得越来越没有价值,在这种情况下,缓存应该时不时地清除掉没用的项,这种工作可以交给一个后台线程(可能是Timer或者ScheduledThreadPoolExecutor)来完成,或者也可以在给缓存添加新条目的时候顺便进行。LinkedHashMap类利用它的removeEldestEntry方法可以很容易的实现后一种方案,对于更加复杂的缓存,就只能直接使用java.lang.ref了。

内存泄漏的第三种常见来源是监听器和其他的回调函数。如果客户在你实现的API中注册回调,但是却没有显示取消注册。那么除非你采取些手段,否则它们就会积聚。确保回调立即被当作垃圾的最佳方法是只保存它们的弱引用,例如,只将他们保存为WeakHashMap中的键。

WeakHashmap 的简单介绍

1. 以弱键 实现的基于哈希表的 Map。在 WeakHashMap 中,当某个键不再正常使用时,将自动移除其条目。更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。丢弃某个键时,其条目从映射中有效地移除

2. WeakHashMap 类的行为部分取决于垃圾回收器的动作。因为垃圾回收器在任何时候都可能丢弃键,WeakHashMap 就像是一个被悄悄移除条目的未知线程。特别地,即使对 WeakHashMap 实例进行同步,并且没有调用任何赋值方法,在一段时间后 size 方法也可能返回较小的值,对于 isEmpty 方法,返回 false,然后返回true,对于给定的键,containsKey 方法返回 true 然后返回 false,对于给定的键,get 方法返回一个值,但接着返回 null,对于以前出现在映射中的键,put 方法返回 null,而 remove 方法返回 false,对于键 set、值 collection 和条目 set 进行的检查,生成的元素数量越来越少。

3. WeakHashMap 中的每个键对象间接地存储为一个弱引用的指示对象。因此,不管是在映射内还是在映射之外,只有在垃圾回收器清除某个键的弱引用之后,该键才会自动移除。

例3

BufferedReader br=newBufferedReader(newFileReader(file));//构造一个BufferedReader类来读取文件

String s =null;

String s1 =""

while((s = br.readLine())!=null){//使用readLine方法,一次读一行

s1 = s1+s

result.append(System.lineSeparator()+s);

}

此例是曾经写过的一个列子,用s用来读取文件中的行的内容,用s1进行接收,当文件中的内容过多时,会导致内存溢出

PS:补充一下堆栈的基础知识。

栈(stack),有时候我们也称为堆栈(这一点可能会让很多小伙伴迷茫)。是由操作系统自动分配和释放的,用来存放局部变量,基本类型的值(比如int,char,boolean等),因为它是操作系统自动分配和释放的,所以通常我们看不到栈的操作。另外,栈是先进先出的。

堆(heap)。由程序员自己来分配和释放。用来存放用new创建的对象和数组。注意,前面我们说了“由程序员自己来分配和释放”,实际上在Java里面,是由程序员自己来分配的(new),但是不是由程序员自己来释放的,而是通过GC(垃圾回收器)来完成释放的,程序员完全感知不到。

方法区(method)。又叫静态区,用来存放在整个程序中都是唯一的元素,比如所有的class,以及static变量

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

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,531评论 18 399
  • 问题的引出 这段程序有一个“内存泄露”,随着GC活动的增加,或者由于内存占用的不断增加,程序性能降低会逐渐表现出来...
    每天学点编程阅读 511评论 0 4
  • Java引用的种类 1.对象在内存中的状态 对于JVM的垃圾回收机制来说,是否回收一个对象的标准在于:是否还有引用...
    Jack921阅读 3,830评论 0 3
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,156评论 11 349
  • 放寒假我和妈妈去外婆家度假,外婆家有很多好玩的、好吃的。我每天都要去探索。有一天我发现,刚吃完食的公鸡却在吃石子我...
    灵济四2阅读 282评论 0 1