问一个 Java 内存泄漏的问题

2015-10-19 10:47:01 +08:00
 RangerWolf
参考的文章: http://www.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/

代码如下:
Vector v=new Vector(10);
for (int i=1;i<100; i++)
{
Object o=new Object();
v.add(o); // 所有的 Object 对象都没有被释放,因为变量 v 引用这些对象
o=null;
}


我的问题就是,当运行这一段程序的 java 进程退出之后,内存泄漏还存在吗?
4006 次点击
所在节点    Java
27 条回复
yonka
2015-10-19 10:55:06 +08:00
java 里没有全局变量,所以从可达性来说,你的 v 估计会被回收吧。
除非在线程 local 、静态属性、 spring context 等中...
zjengjie
2015-10-19 11:16:30 +08:00
进程退出后,内存会被操作系统回收。
anexplore
2015-10-19 11:25:14 +08:00
不存在了;这里面的泄露是发生在 jvm 管理的堆里面,其实你在程序中将 v = null;就可以通过 gc 回收内存了
iyangyuan
2015-10-19 11:45:15 +08:00
进程? jvm 进程在一个容器里只有一个,楼主应该是在说线程,线程退出后很可能还存在,否则就不会泄露了具体看你代码怎么写,很难一概而论。
RangerWolf
2015-10-19 13:24:08 +08:00
@iyangyuan 比如在 linux 里面, 使用 top 命令 看到 command 列 不止一个 java~ 这是进程还是线程?
HunterPan
2015-10-19 13:42:57 +08:00
@RangerWolf 进程
iShao
2015-10-19 14:53:05 +08:00
学过 java 但是怎么这段代码看不懂了…现在用 OC
Cloudee
2015-10-19 15:01:20 +08:00
就这段代码而言,什么时候 v 释放了,里面东西就释放了
cnhongwei
2015-10-19 15:02:20 +08:00
@RangerWolf JVM 只有一个进程,是多线程的,在 Linux 中看到多个进程,是因为 Linux 使用多进程来模拟多线程的(不要以为这个代价高、性能低哟)。

回复楼主的就是 最重要的是 v 你放到什么地方了,只是没有放到静态属性中(或其它类似的地方,如 web 应用中 application , spring 中的 context)中就行了。 java gc 时,大部分的垃圾回收器,都是从在使用的类与实例开始遍历,遍历完后,没有被遍历上的实例都认为是没有用的,直接全部回收就行了。
Cloudee
2015-10-19 15:02:42 +08:00
另外进程退出所有的内存和文件句柄都会被操作系统释放掉,这个是和 java 没关系的
domty
2015-10-19 15:06:44 +08:00
不好说啊,说句废话, java 的垃圾回收主要依赖 jvm 的实现。
所以回收的时机本身是不确定的。当你的这段代码块执行结束后,代码块内的对象没有在代码中没有被引用的话,应该就是处于一个待回收的状态。如何没有手动触发 GC 的方法的话,是不知道 jvm 是何时回收这部分内存的。
好久以前看过的一部分 java 虚拟机知识了,所以有点记不到了,不确定说的是不是对的..
domty
2015-10-19 15:14:11 +08:00
@domty
句子写的有问题,
代码块内的对象没有在代码中没有被引用的话 应是 代码块外的代码中不再存在代码块内对象的引用
RangerWolf
2015-10-19 17:01:06 +08:00
@cnhongwei 那 JVM 在 windows 之中呢? 应该是多进程了吧? 原来多个 java 是模拟出来的,第一次知道,学习了。。。

另外,那比如 public static final String a = 'aaaa' 类似这种变量,会内存泄漏吗?
RangerWolf
2015-10-19 17:02:01 +08:00
@domty 想问下 你是从哪边看的资料? 我也去学习学习,多谢!
honam
2015-10-19 22:19:48 +08:00
弱弱的问句,即使程序不退出,也不会造成内存泄露吧? v add 的不是 o 的引用而已嚒, o 为 null 了那 v 里面全是 null 喇不是吗?菜鸟一只大神勿喷,求解
CRVV
2015-10-19 22:40:45 +08:00
@honam
Java 所谓的引用和 C++ 的引用完全是两回事
把 Java 所谓的引用当指针来看,一切都简单明了清楚易懂了
honam
2015-10-19 22:42:51 +08:00
@CRVV 谢谢回答,还想问,那 List 实现的容器比如 ArrayList 或者 LinkedList 都会有这些问题?
CRVV
2015-10-19 23:14:19 +08:00
@honam
我印象中 Vector 在非远古版本的 Java 里,就是一个线程安全的 ArrayList
其实写一下就明白了,下面输出 45 和 null
Integer a = 45;
Integer b = a;
b = null;
System.out.println(a);
System.out.println(b);

等价的 C 代码是:
int a = 45;
int* x = &a;
int* y = x;
y = NULL;

这并不是一个问题,很多语言都是这么设计的,只不过通常不像 Java 一样非要把这种变量类型叫“引用”
rundis
2015-10-20 07:56:27 +08:00
进程退出之后是肯定不能够泄露的,都被回收了
运行中想回收掉 v 就直接
v = null
建议不要 System.gc()跑 gc

另外 vector 和 arraylist 有点不同,属于遗留集合了吧,看看接口都不是常用的集合接口的
cnhongwei
2015-10-20 09:06:10 +08:00
@RangerWolf 不好意思,上面说的有误, Linux 线程实现有几种,其中一种是使用进程来模拟线程,但使用 ps 看到的还是一个进程。

public static final String a = 'aaaa' ,这种,类加载器会把 'aaaa' 放到常量池中,类加载后,内存就不会释放,但不算是内存泄漏,这是因为:我们说的内存泄漏,主要是指动态分配了内存,不再需要的时候,应能释放,如果没有办法释放,就算内存泄漏,所以,比如一些程序就需要很大的内存空间,但这是正常使用,就不算内存泄漏。

如果连接池之类的这种池化技术,就是让一些资源不释放,使得下一次使用的时候速度更快,这就是故意这样设计的,这就不算内存泄漏。

但如果我是做一个程序,把文件文件中行读到一个 List 中去,使用完后,这个数据没有用了,应释放,但因为自己到这个 List 定义到一个类的 static filed 中去了,所以不会被 gc 到,这就算内存泄漏了。

内存泄漏也是相对的, 比如程序就那么几个泄漏,程序调用一次后也没有再调用过,泄漏了也没有关系,怕的就是频繁调用的地方有泄漏,最后让 jvm crash 了,这就严重了。

写程序的时候,一般的普遍 object (除超大的 String 外),泄漏也没有什么大的关系,最容易是 jvm crash 的是,把 object 不停的加入到 Collection 中。所以写程序注意一下超长的 String 及 Collection 的应用,少使用 static 变量(常量不算内存泄漏, static 作为性能优化常用,但在现代的 jvm 优化技术下,基本没有太大的必要),一般不会内存泄漏。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/229122

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX