V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
RangerWolf
V2EX  ›  Java

问一个 Java 内存泄漏的问题

  •  
  •   RangerWolf · 2015-10-19 10:47:01 +08:00 · 3977 次点击
    这是一个创建于 3084 天前的主题,其中的信息可能已经有所发展或是发生改变。
    参考的文章: 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 进程退出之后,内存泄漏还存在吗?
    27 条回复    2015-10-20 15:05:26 +08:00
    yonka
        1
    yonka  
       2015-10-19 10:55:06 +08:00   ❤️ 1
    java 里没有全局变量,所以从可达性来说,你的 v 估计会被回收吧。
    除非在线程 local 、静态属性、 spring context 等中...
    zjengjie
        2
    zjengjie  
       2015-10-19 11:16:30 +08:00   ❤️ 2
    进程退出后,内存会被操作系统回收。
    anexplore
        3
    anexplore  
       2015-10-19 11:25:14 +08:00
    不存在了;这里面的泄露是发生在 jvm 管理的堆里面,其实你在程序中将 v = null;就可以通过 gc 回收内存了
    iyangyuan
        4
    iyangyuan  
       2015-10-19 11:45:15 +08:00 via iPhone
    进程? jvm 进程在一个容器里只有一个,楼主应该是在说线程,线程退出后很可能还存在,否则就不会泄露了具体看你代码怎么写,很难一概而论。
    RangerWolf
        5
    RangerWolf  
    OP
       2015-10-19 13:24:08 +08:00
    @iyangyuan 比如在 linux 里面, 使用 top 命令 看到 command 列 不止一个 java~ 这是进程还是线程?
    HunterPan
        6
    HunterPan  
       2015-10-19 13:42:57 +08:00
    @RangerWolf 进程
    iShao
        7
    iShao  
       2015-10-19 14:53:05 +08:00 via Android
    学过 java 但是怎么这段代码看不懂了…现在用 OC
    Cloudee
        8
    Cloudee  
       2015-10-19 15:01:20 +08:00 via iPhone
    就这段代码而言,什么时候 v 释放了,里面东西就释放了
    cnhongwei
        9
    cnhongwei  
       2015-10-19 15:02:20 +08:00   ❤️ 1
    @RangerWolf JVM 只有一个进程,是多线程的,在 Linux 中看到多个进程,是因为 Linux 使用多进程来模拟多线程的(不要以为这个代价高、性能低哟)。

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

    另外,那比如 public static final String a = 'aaaa' 类似这种变量,会内存泄漏吗?
    RangerWolf
        14
    RangerWolf  
    OP
       2015-10-19 17:02:01 +08:00
    @domty 想问下 你是从哪边看的资料? 我也去学习学习,多谢!
    honam
        15
    honam  
       2015-10-19 22:19:48 +08:00
    弱弱的问句,即使程序不退出,也不会造成内存泄露吧? v add 的不是 o 的引用而已嚒, o 为 null 了那 v 里面全是 null 喇不是吗?菜鸟一只大神勿喷,求解
    CRVV
        16
    CRVV  
       2015-10-19 22:40:45 +08:00
    @honam
    Java 所谓的引用和 C++ 的引用完全是两回事
    把 Java 所谓的引用当指针来看,一切都简单明了清楚易懂了
    honam
        17
    honam  
       2015-10-19 22:42:51 +08:00
    @CRVV 谢谢回答,还想问,那 List 实现的容器比如 ArrayList 或者 LinkedList 都会有这些问题?
    CRVV
        18
    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
        19
    rundis  
       2015-10-20 07:56:27 +08:00 via iPhone
    进程退出之后是肯定不能够泄露的,都被回收了
    运行中想回收掉 v 就直接
    v = null
    建议不要 System.gc()跑 gc

    另外 vector 和 arraylist 有点不同,属于遗留集合了吧,看看接口都不是常用的集合接口的
    cnhongwei
        20
    cnhongwei  
       2015-10-20 09:06:10 +08:00   ❤️ 1
    @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 优化技术下,基本没有太大的必要),一般不会内存泄漏。
    RangerWolf
        21
    RangerWolf  
    OP
       2015-10-20 10:18:09 +08:00
    @CRVV 具体我不是非常了解,应该是属于 可达 无引用?
    RangerWolf
        22
    RangerWolf  
    OP
       2015-10-20 10:21:23 +08:00
    @cnhongwei 您说的非常详细,学习到很多东西,非常感谢!
    domty
        23
    domty  
       2015-10-20 10:59:56 +08:00
    @CRVV
    java 是彻底的值传递,不存在引用传递的。
    同理 java 里也没有传指针的概念。
    Integer a = 10; //声明了一个 Integer 指向 Integer 型变量 10 。
    Integer b = a; //声明了一个变量 b ,指向的地址和 a 指向的地址相同。
    a = 20; //改变了 a 指向的地址, a 的指向为一个 20 的 Integer 型变量。这个时候 b 的指向还是 Integer 型 10 的地址。

    这也是为什么 java 无法实现 swap(a,b)方法来交换两个变量的值得原因。
    xiuc001
        24
    xiuc001  
       2015-10-20 11:04:19 +08:00
    进程是操作系统创建的,进程没了,相应的堆栈信息都会被收回,而 java 程序是跑在这个进程中的,堆栈也是在进程的堆栈中,所以会被一起收回。。
    简单的说 一起被收回~
    CRVV
        25
    CRVV  
       2015-10-20 11:23:42 +08:00 via Android
    @domty
    所以在你举的例子里,把所有的变量都当成指针,是不是完全符合 Java 的行为?
    所以我前面用 C 的指针来类比 Java 的变量
    关键是搞清楚语言的语义,至于有没有传指针的概念,我认为并不重要
    wwqgtxx
        26
    wwqgtxx  
       2015-10-20 13:12:59 +08:00 via Android
    @domty 对于对象是完全的引用传递,对于基础类型才是值传递,不要一概而论
    sunnyhust2005
        27
    sunnyhust2005  
       2015-10-20 15:05:26 +08:00
    首先关于内存泄漏的概念需要澄清, Java 与 C/C++在这点上有很大的区别。从技术上来说, Java 不会出现 C/C++/Objective-C 所出现的内存泄漏。当在堆中分配的对象不再有指针指向时,就发生了内存泄漏;而对 Java 而言,不再有引用指向对象会被 GC 清除,所以不会有内存泄漏的问题。如果硬要认为 Java 也有内存泄漏,可以理解为“当对象的生命周期已经结束,但是有引用指向它,导致它无法被 GC 从堆中释放”。而后者和前者效果相同:都是内存中保留了不再需要的对象。

    具体来看这个问题,在对局部变量 o 赋值为 null 前, Vector 对象在内部的数组里已经保存下来了 o 引用,所以不会发生 Java 意义上的内存泄漏。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5406 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 40ms · UTC 07:00 · PVG 15:00 · LAX 00:00 · JFK 03:00
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.