http://bbs./article/Java/3960?p=1 发信人: Aaron12 (【学盟】12), 信区: Java 标 题: [原创]难道Java里不需要手动释放内存? 发信站: 北邮人论坛 (Tue Apr 15 21:32:39 2008), 站内 java 有了GC,告诉程序员当内存不够的时候GC会自动的回收、释放一些不要需要的内存。 往往给初学者(尤其是我)造成很大的误区: java世界的里的内存是无限的! new 了对象之后就完全不需要考虑它的回收、释放。 而现实告诉我,这是绝对的错误的! 看两个例子: 1、使用JDBC,导致out of memory 有人认为是因为生成了非常多的connection,导致程序内存不足。但即使 在一个程序(单线程)里只使用一个connection,仍然会出现内存不足,原因是生成过多的 PreparedStatement (pstm), pstm的创建也是非常耗资源的,如果不手动关闭它,那只有等关闭connection时才能关闭(关闭connection时会关闭所有由该 connection 创建出来的pstm). 所以当数据量较大时,即使只使用一个connection也会造成pstm的过多创建。 因 此 程序员 就必须 :共用 pstm(和共用connection 一样)或者 适时手动关闭 pstm。 2、 使用runtime.exec("shell") 我在使用runtime运行shell convert (一个处理图片的程序)命令时,依然没有释放processor,当处理到300个图片时,出现了open too many files 的异常。使用top命令一看,居然有10个左右的convert进程在跑(崩溃了~~~~)。 后来发现:因为每个命令都有一个错误流 (返回执行的结果),只有对这个错误流进行了处理,processor才能及时的释放。 例子1 告诉我们: 某个pstm即使不再使用,但只要创建该pstm的connection依然使用,GC是不会收回pstm。这和GC的原理是一样的。 所 以我们在写程序时,就必须牢记这点,这是最基础也是最容易出错的,尤其是在一个大循环的情况下。 例子2 告诉我们: 在Java程序里,jvm只是其中的一部分,我们必须要考虑和jvm一起合作的其他部件。 比如例子2中的convert命令、unix文件系统。我们必须要知道用java和他们一起工作会出现什么问题。 比如:文件的连接数最多是多少? 在正常、异常情况下应该如何适时的关闭连接数? 比如:2003 sqlserver的本地连接数是多少(测试过一次,每循环一次就创建一次连接,循环了600多次,就无法再继续建立连接了),使用完 connection后 就需关闭它 或者 使用 连接池 缓存起来。 所以使用java和其他组件合作时,就必须明白其他组件的一些硬性条件,而不能只生存在无限量内存的java 的虚拟环境中。
发信人: UnitTest (TDD), 信区: Java 标 题: Re: [原创]难道Java里的不需要手动释放内存? 发信站: 北邮人论坛 (Wed Apr 23 14:00:09 2008), 站内 嗯嗯,对头,即使只是纯粹的内存资源分配,你也不能生存在无限内存的Java世界里面。 资源分两种,托管资源 和 非托管资源。而往往通过一个对象申请非托管资源的时候,对象本身就占用了托管资源。比如FileStream,它的非托管资源当然就是打开的文件,而托管 资源当然就是这个对象在托管堆上所占的内存,还有FileStream所申请的缓冲区内存。而GC是只负责收集托管资源的,至于非托管资源必须由用户来显 式释放。换句话来说,即使你把FilStream这个对象所打开的文件close掉,也就是释放了非托管资源,但是它所占的托管资源还在,依然得由GC来 释放,实际上是两次释放资源的过程。 发信人: zooloo (zj), 信区: Java 标 题: Re: [原创]难道Java里不需要手动释放内存? 发信站: 北邮人论坛 (Thu Apr 24 09:52:21 2008), 站内 我认为你把GC处理问题范畴搞错了! GC是用来处理没用的对象,而不是用来处理没用的资源(你举的两个例子)。 最著名的例子是Effective Java里堆栈的pop操作(当对象的实际生命周期与它自己本身的生命周期出现不一致的时候,要考虑消除过期的引用)。 而你的例子只是说,要使用try{}finall{}来处理回收资源的问题。 发信人: UnitTest (TDD), 信区: Java 标 题: Re: [原创]难道Java里不需要手动释放内存? 发信站: 北邮人论坛 (Thu Apr 24 13:37:59 2008), 站内 【 在 pmps 的大作中提到: 】 : 好像本地资源托管环境是不管的吧,还是需要手动回收的。 恩, 对头,其实像C++那种new出来的内存,实际上是由系统给分配的,同时也是由系统管理的。至于它的生命期除非你在程序里显式请求(delete)系统释 放它否则就是系统说了算,所以本地内存也是非托管资源。 回来Java里面,做这么一种假想,如果我们用C++来开发jvm,那应该怎么做?我们现在只考虑托管堆的实现。很常见的一种解决方案是这样的,程序启动 的时候,首先我们用C++编写的jvm应该通过C++的new跟系统申请一大块内存区域。然后每次jvm从这一整块内存区域上面找一小块用来生成新对象。 当对象被垃圾回收的时候,jvm只是将对象所占的这块内存标记为未占用的,实际并没有将它返回给系统,下次构造新对象的时候再将这块标记为未占用的内存分 给其它对象。很明显,这个时候你可以发现java对象是在一大块你自己管理的内存区域上面构造的,你可以随意指定java对象应该分配在这块内存区域上面 的哪个位置,C++的语法直接支持这个功能。所以我们将这块区域称为托管堆,也就是由你用C++编程管理的堆。当然程序结束的时候,由jvm将这块内存整 体返还给系统。 至于非内存的其它资源,比如文件,套接字,网络连接,互斥体等,这些其实和上面所说的托管堆差不多,都是跟系统借来的。但是这些资源一方面在很多程序里面 至少不会频繁出现,另一方面种类繁多,真要像上面的内存那样托管起来可是一件相当麻烦而且没有必要的事情。所以Java最后面还是将这些资源的释放任务交 给了用户,所以我们将它们称为非托管资源 总而言之,java就是跟系统要了一块场地,搞了一个所谓的托管的小帮派,宣扬"一切皆OO"的教义...... ps:我没有去了解过jvm,以上只是我学完C++做的一点猜想,不当之处还请指出 、 (#) |
|