问一个 Java 内存泄漏的问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
RangerWolf
V2EX    Java

问一个 Java 内存泄漏的问题

  •  
  •   RangerWolf 2015-10-19 10:47:01 +08:00 4651 次点击
    这是一个创建于 3646 天前的主题,其中的信息可能已经有所发展或是发生改变。
    参考的文章: 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
    RanerWolf  
    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     Solana     5992 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 35ms UTC 02:11 PVG 10:11 LAX 19:11 JFK 22:11
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86