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

为什么创建的对象在离开作用域后没有被回收?

  •  
  •   myyou · 2019-02-13 17:45:38 +08:00 · 2737 次点击
    这是一个创建于 2105 天前的主题,其中的信息可能已经有所发展或是发生改变。
    • Golang 测试代码:
    import (
    	"fmt"
    	"time"
    )
    
    func main() {
    	for i := 0; i < 10; i++ {
    		c := make([]int32, 0, 1000000000)
    		fmt.Println(len(c))
    	}
    	fmt.Println("====ok====")
    	time.Sleep(60 * time.Second)
    }
    
    • Java 测试代码:
    import java.util.ArrayList;
    import java.util.concurrent.TimeUnit;
    
    public class TestArray {
        public static void main(String[] args) throws InterruptedException {
            for (int i = 0; i < 10; i++) {
                ArrayList<Integer> al = new ArrayList<Integer>(1000000000);
                System.out.println(al.size());
            }
    
            System.out.println("====ok====");
            TimeUnit.MINUTES.sleep(1);
        }
    }
    

    这两个在做完 for 循环后,停顿一分钟内存依然没被回收。

    第 1 条附言  ·  2019-02-13 19:08:51 +08:00
    补充:并不是说程序退出了内存没回收,而是在 sleep 等待一分钟内,内存一直没回收。
    按理说,for 循环结束后,gc 应该回收掉已创建的且没有使用的对象的内存,但是在这一分钟等内存一直都没有回收。
    第 2 条附言  ·  2019-02-13 19:26:26 +08:00
    无论是 golang 的 runtime.gc()还是 java 的 System.gc(),直接调用都不回收内存(不知道是不是需要调用多次才会有效?
    )。
    sleep 时间拉长的话的确观察到内存有回收。
    sufan
        1
    sufan  
       2019-02-13 19:04:35 +08:00 via iPhone
    清洁工还没有来
    chenyu0x00
        2
    chenyu0x00  
       2019-02-13 19:07:35 +08:00 via Android
    go 和 java 的内存回收都不是实时进行的
    myyou
        3
    myyou  
    OP
       2019-02-13 19:12:37 +08:00
    @chenyu0x00 不是实时进行的,也应该在一定时间内进行吧
    alamaya
        4
    alamaya  
       2019-02-13 19:13:30 +08:00
    你需要 System.gc(),多运行几次
    337136897
        5
    337136897  
       2019-02-13 19:13:32 +08:00
    清洁工还没空来
    lhx2008
        6
    lhx2008  
       2019-02-13 19:14:48 +08:00 via Android
    内存回收需要暂停程序的,所以一般是到了阈值才回收,楼主可以用 System.gc()强制回收
    chenyu0x00
        7
    chenyu0x00  
       2019-02-13 19:18:38 +08:00 via Android
    @myyou 毕竟内存回收是需要消耗算力的,而且 go 的内存回收还会暂停主程序,所以在内存充足的时候可以不进行内存回收。如果想要测试内存回收,你可以写一个函数,函数内部不断申请内存,然后在主函数不断调用该函数,看看程序的内存会不会无限增长。
    myyou
        8
    myyou  
    OP
       2019-02-13 19:24:12 +08:00
    @chenyu0x00 把 sleep 时间拉长,的确有回收.

    @lhx2008 无论是 golang 的 runtime.gc()还是 java 的 System.gc(),直接调用都不回收内存,sleep 时间拉长的确有回收。
    neoblackcap
        9
    neoblackcap  
       2019-02-13 21:17:25 +08:00 via iPhone
    System.gc 只是给 JVM 提建议,JVM 完全可以不鸟你。都是符合规范的。比如 JVM 的堆有 1T 大,你才用了 1G 就调用 System.gc ,JVM 当然可以不鸟你。而且回收策略都不尽相同,你换成 G1 跟 ZGC 又不一样了
    zwh2698
        10
    zwh2698  
       2019-02-13 22:33:28 +08:00 via Android
    9 楼正解,gc 凭啥你说回收就回收呢?平衡策略怎么办?
    wweir
        11
    wweir  
       2019-02-14 05:47:55 +08:00 via Android
    就这两份测试代码而言,今天我看到任何对堆栈的检测。如果没猜错的话,楼主是通过操作系统的命令来观测内存占用信息的。

    程序从系统中分配的内存,与代码从 runtime 中分配的内存是两回事。
    GC 只会把堆栈中的分配的内存交还给 runtime,而 runtime 中有内存复用的逻辑,具体什么时候交还给操作系统,有另外一套逻辑单独控制。

    市面上介绍 runtime 的内存申请、复用、释放具体思想的书、文章很少,所以这一点经常会被人忽略
    wweir
        12
    wweir  
       2019-02-14 05:49:53 +08:00 via Android
    就这两份测试代码而言,我没有看到任何对堆栈的检测。

    别字修正
    snappyone
        13
    snappyone  
       2019-02-14 09:33:00 +08:00
    建议看下 gc 的触发原理,不是看时间跟 System.gc 调用的
    micean
        14
    micean  
       2019-02-14 09:51:42 +08:00
    jvm 的信息都看不到
    dosmlp
        15
    dosmlp  
       2019-02-14 10:12:07 +08:00
    说明内存够,不需要进行回收
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1123 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 18:45 · PVG 02:45 · LAX 10:45 · JFK 13:45
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.