第一个例子,是因为element数组里还保留着对象的引用。正确的方法是把栈顶元素置为null,再递减size。当Stack生命期结束时,一并释放。第二个例子中,是指在你删除最后一个Listener和你添加一个新的Listener之间这段时间内,数组里仍然保存着上一个Listener的引用。相比第一个例子而言,这个问题倒不是显得那么严重。

解决方案 »

  1.   

    to: buaaaladdin(阿拉丁的灯) 
    你好象还没明白我的意思啊?
    第一个例子,数组里是保留着引用,但是,我是说,如果Stack生命结束后,
    GC是否会回收?如果会回收,我认为这就不算泄漏。第二个例子要比第一个严重,因为可能类EventListenerList死亡后,里面的
    元素还没死,还被引用。
    第二例中,作者就是跟你说的那样:
    public class EventListenerList
    {
       //
       // etc.
       //
       public void remove(EventListener listener)
       {
          int index = find(listener);      if (index < 0)
             // error condition - do something about it      for (int n = index; n < size - 1; ++n)
             listeners[n] = listeners[n + 1];      listeners[size - 1] = null;
          --size;
       }

    这当然没问题,但我还是不明白,为什么里面的元素在没有引用(?)时还不被回收,
    我怀疑别的地方还有引用,如Listener注册到的界面元素是否对这个Listener有引用?
      

  2.   

    第一个例子很正常,没什么问题。只是可能对象数组是满的(initialCapacity个对象),虽然堆栈很小(size<initialCapacity),但是堆栈顶上面的数据还是继续被引用。也就是说只要对象数组满过一次,以后随时都是满的。要解决的话,POP的时候,返回对象的同时,赋值NULL第二个例子也存在这样的毛病。而且第二个例子这种情况最好就是用集合:ArrayList或Vector,方便很多,取数据的时候不需要挨个移动。
    另外Listener如果被addXXXListener,就被一个组件注册了,那么它的引用也会被该组件持有,而组件一般是按钮等可视构件,和应用程序生命期一样长,所以如果不被组件removeXXXListener,这些Listener对象也将一直存在下去。
      

  3.   

    谢bigcrazy(大疯狂)。
    第一个例子象是《Effective Java》中的。可参见http://courses.dce.harvard.edu/~cscie160/EffectiveJava.htm
    第一例中,无非是一个很满的数组,有些不用的元素在那耗着浪费,这我
    明白。我只是认为,如果Stack死亡后,GC会全部收回内存,就不算内存泄漏.(???)第二例,作者没有说明为什么Listener就会泄漏。只能“想象”它被注册组件引用。
    这样,这信EventListenerList死亡后,这些Listener还在。如果没有谁有意见,这就是结论了?
      

  4.   

    你的理解有一个错误,就是如果stack,eventlistenerlist死亡就不会有泄漏。但是他们是如何死亡的?很多情况下这种类都是会被保存直到整个程序被终止,system.exit(0)他们才会死亡。就算上面我说得不成立,你的死亡条件是怎样的呢?不需要的时候内存还被长时间占用就是内存泄漏
      

  5.   

    其实把java的引用当成是c/c++的指针问题就好理解了
    第一个问题 ,pop时只是返回栈顶的元素,没有释放内存(没有把它置为null。其实置为null只是失去对元素的引用,等着gc来收集了,并没有真正释放内存),当然有内存泄漏;
    第二个问题类似,只是不只是内存资源的浪费,问题更严重。
      

  6.   

    作者之所以写Effective java这本书就是为了提出一些影响java程序运行效率的Item,给出合适的解决办法。以便我们可以写出更高效的java程序。第一个问题就是典型的例子,我想楼主也明白这个程序的缺点了,高手也都指出来了。其实有java的垃圾回收的机制,是不会出现memory leak的情况的。早回收晚回收都是会收回的,等到拉机手机器感觉到内存不够用了,它会停止程序的其他线程来运行垃圾收集器,当然也有并行处理的垃圾回收器的实现。但是垃圾回收机制让java程序丧失了很多效率的。比如这个例子里面  POP出来以后,这个元素不被设置为null的话,它就不会被放到垃圾回收的队列中,如果stack的生命周期比较长的话,那么它可能会被垃圾收集器放到old generation里面去,这里面的对象很少有机会被运行垃圾收集来收回的,如果是一个Object我们或许可以忍受,如果是多个的话,我们的程序就占用大量的内存。机器越来越慢。
      

  7.   

    mingjava(学习):
    这是上面这位兄弟这么说。
    那你怎么说呢,你不觉得上面的程序是“泄漏”?
    虽然那不是好程序,但我也觉得不算泄漏。
    在JNI中,有些内存是虚拟机控制不到的,即使你的java程序结束了,
    有一些内存也不能回收,我觉得这才算是泄漏。
      

  8.   

    讨论:
    第一个问题:pop时,没有释放elements中的对象,只是返回了elements中一个的对象引用。也就是说这个队列实际上只能向里面加对象,而没有删除对象的方法。
    就象你说的,如果你程序中使Stack生命期结束了,这些对象可能被释放,也可能不会释放。
    想一下:当你pop了一个对象,实际上size指向顶层下一个对象,但顶层的还存在,此时如果push了,先将扩充一个集合,然后将size的位置指向新的对象,那原来的的那个对象还存在,但已经没有对象的引用指向它了,这里产生内存泄漏。如果你先pop了多个,在push,内存还要多泄漏。
      

  9.   

    第二个问题:
    产生的原因也是和第一个一样的。
    listeners[n] = listeners[n + 1];实际上也没有释放原来的listeners[n]对象,而产生了一个没有被引用的对象,即这个对象不可能被回收。
    下面的英文,大致也是这个意思,描述了产生泄漏的条件。还有:java里关于对象的概念和c里的指针有很大的不同,在初学的时候可以这样去快速理解,但写过一些程序后就会体会到他们的不同。
    关于释放这些对象,也不是简单地赋为null就行了的。
      

  10.   

    bluesmile979(笑着) 兄的说法不妥当,内存泄露,就是指内存不能被任何程序,当然包括操作系统所控制的,叫做内存泄漏哦。java,如果内存泄漏,那只能是jvm的问题,跟咱编写程序没有关系。另外我还是建议,大家如果没有十足的把握,最好不要自己去管理内存。因为既然你选择了java,那么你再怎么去管理内存,你都不可能突破jvm对内存的管理。
      

  11.   

    楼上的老大:
    虽然通常情况java不必我们关心内存。
    但程序处理不好照样会有内存泄漏,就像楼主的题目。
    另外
    楼主:如果程序中想写数据结构,最好用java自己提供的集合来实现,它们一般都提供了分配和释放对象包含的方法。
      

  12.   

    靠。。程序有问题吧。。!
    第二个程序里。。
    1,find()函数的第一个return 语句笔误了吧。是return n;
    2,remove()函数里,如果是移除最后一个listener,
    语句"listeners[n] = listeners[n + 1];"数组越界了吧不知小弟有没看错呢?
    不过JVM会怎么处理Object数组里的东西,我还是不怎么明白,候教中如果JVM的垃圾回收决定要回收此对象数组,它是简单的置null,把数组占的内存收掉,还是会顺便把具体的元素(不在有其它对象对它的引用)也收走呢,,,?
      

  13.   

    to coolcoot(紫黑蓝白):
    1,find()函数的第一个return 语句笔误了吧。是return n;
    2,remove()函数里,如果是移除最后一个listener,
    语句"listeners[n] = listeners[n + 1];"数组越界了吧
    ---------------------
    我是copy过来的。find的我看也是 return n;
    不过,第二个问题,应该是没有越界。因为是 n<size-1的,就是
    n最大才size-2。
      

  14.   

    bluesmile979(笑着) 兄的说法不妥当,内存泄露,就是指内存不能被任何程序,当然包括操作系统所控制的,叫做内存泄漏哦。可能我的说法不太妥当,但是我仍然坚持我的观点。我是做嵌入式开发的,有的时候只是一个函数内部的循环处理就要考虑内存的释放问题。按大家的意思,函数执行完了不久都死亡了么。但是对于大的对象,对于那有限的资源,这同样是一种形式内存泄漏。虽然这可能不是官方严格定义的内存泄漏。而且如果有兴趣大家可以自己去看看GC的工作原理。如果这种代码多了,你的程序就有好看了。
      

  15.   

    何必为它是不是内存泄漏争呢?既然知道了这个代码的问题,那么去修正它就是了。这个肯定不是内存的泄漏,也确实对我们的程序不利。写出好的java程序才是我们的目的
      

  16.   

    叫内存泄漏当然不妥,目前还没有一个正式的官方术语表达这个问题. 大家理解它
    所包含的意义就行了. "irrelevant memory holding"似乎合适一点. 关于这个一
    点确实要注意,不要把GC想像的那么美妙,其实"它很傻", 如果在更合适的时候,发
    出相比较更明确的指令驱动它,则是非常认真负责的做法(当然,就算你这么做了,
    GC买不买你账还另当别论那,GC的实现是平台相关的). 正如有的兄弟谈到,在一些
    要求比较高的situations(例如:长期运行的程序,频繁调用的方法,多线程并发处理
    模块,内存容量苛刻的环境等),最好手动做些工作,就能或多或少地弥补GC比较傻这
    一事实所带来的负面影响. 当然我说GC傻是个不太负责任的说法,GC的工作原理比较
    复杂,要考虑的事情非常多,除非是sun的这方面的专家才有资格对它评头论足啊.我
    只是凭普通人"看上去"的方式说的. 例如,GC提供了一种机制,让应用可以重新发现
    已经走上"适合被收集"之路的对象,并做出有关处理. 虽然我还不知道这样的feature
    有什么具体应用价值,但sun费劲地开发并提供了这样的功能,说不定就有啥用,呵呵.
    可以说有JNI的地方,就存在内存泄漏,只是多少而已. 包括jvm内部的JNI,照样存在
    内存泄漏,我看过这方面的研究文章,其中最厉害的两个方面是JNI实现的数据库操作
    和平台相关的重量级图形控制. 我自己开发了CWJTray大量利用JNI实现,其中涉及
    双向互传对象、jvm回调、JNI图形控制、线程同步,才感受到内存泄漏真的是个很
    严重的问题,也是个非常难于控制的问题。好在不是什么大型软件。试想,象一般
    的大些的软件,如Weblogic,Websperhe,JBuilder之类,开发者很不容易啊。怪
    不得好值钱啦。
      

  17.   

    bluesmile979(笑着) ( )
    正确
    1。泄漏
    因为add过一个元素以后,这个元素永远不会被清除
    换句话说,只要add过一次,那elements就永远有数据。
    所以stack不会被回收
      

  18.   

    to  allenhe():
    有数据就不会回收?
    有引用才不会回收吧。
      

  19.   

    elements就有数据那不就是维持着引用么
    elements无法被回收,stack会被回收么?
      

  20.   

    to  allenhe():
    你的意思是要数组里的元素都是null,就组才会被回收?搞错了吧?
    是先去掉上层(引用),下层(数组)没有被引用了,才被回收的,也就是先
    回收上层,才回收下层的。
      

  21.   

    楼主太粗心大意,第一题编译报错如下: ^-^D:\>javac Stack.java
    Stack.java:8: cannot resolve symbol
    symbol  : method ensureCapacity  ()
    location: class Stack
    ensureCapacity();
    ^
    Stack.java:13: cannot resolve symbol
    symbol  : class EmptyStackException
    location: class Stack
    throw new EmptyStackException();
              ^
    2 errors
      

  22.   

    effective java上的一个例子?
      

  23.   

    回复人: allenhe() ( ) 信誉:100  2004-04-26 18:29:00  得分:0 
     
     
      bluesmile979(笑着) ( )
    正确
    1。泄漏
    因为add过一个元素以后,这个元素永远不会被清除
    换句话说,只要add过一次,那elements就永远有数据。
    所以stack不会被回收
     
     
    ===================================================楼主的第一个例子有elements无add ,第二个例子有add无elements是不是看错了?
      

  24.   

    回复人: game0ver12345(sfsfdsfdsdfsf) ( ) 信誉:100  2004-04-26 22:50:00  得分:0 
     
     
      回复人: allenhe() ( ) 信誉:100  2004-04-26 18:29:00  得分:0 
     
     
      bluesmile979(笑着) ( )
    正确
    1。泄漏
    因为add过一个元素以后,这个元素永远不会被清除
    换句话说,只要add过一次,那elements就永远有数据。
    所以stack不会被回收
     
     
    ===================================================楼主的第一个例子有elements无add ,第二个例子有add无elements是不是看错了?
     
     
    =============================
    add是增加数组的元素 还是第一个例子里的push方法
      

  25.   

    to  game0ver12345(sfsfdsfdsdfsf) :我们且不谈具体细节。如编译错。这些不问题的实质。
    bluesmile979(笑着)说的add方法,或元素,也可以广义地看作push和其它类型元素。第一例是Effective java上的。
      

  26.   

    回复人: qiuwanbin(哈哈,我呀。) ( ) 信誉:97  2004-04-27 08:04:00  得分:0 
     
     
      to  game0ver12345(sfsfdsfdsdfsf) :我们且不谈具体细节。如编译错。这些不问题的实质。
    bluesmile979(笑着)说的add方法,或元素,也可以广义地看作push和其它类型元素。第一例是Effective java上的。 ======================================================大家都想讨论问题的实质。但是这些错误或多或少的影响大家对问题实质的理解。讨论的时候严谨一些不是更好吗?而且作为一个程序员粗心大意是一个极之不好的习惯。