go java 垃圾回收_Go垃圾回收?

@唐生 大大的回答就是正解了,请给他的回答点赞。关键点就在于“zeroing”——GC堆上申请的空间要被清零,在Linux这种commit on-demand的环境中最迟在“往内存写入0”的动作会触发其对应的内存被实际commit,没那么多内存可用的话直接就要被系统OOM killer干掉了。

我就来跑个题而已。把题主给的Go的例子换成Java,稍微改写一下方便演示。

(注:当然,Go的slice跟Java的数组并不直接对等;硬要说的话它某些方面像Java数组(例如长度不是类型的一部分),而另一些方面像Java的ArrayList(例如带有一层间接,可以支持扩容等)。正因为Go的slice可以扩容,slice这个类型上并没有保证len是不可变的,所以不像Java的数组的length有保证不可变而可以做进一步优化。

不过就题主的例子而言,未来Go的编译器优化能力提升之后,还是有机会达到下面演示的效果的。)

public class DemoEA {

public static final long LEN_IN_GO_DEMO = 10_000_000_000L;

public static final int LEN_IN_THIS_DEMO = (int) LEN_IN_GO_DEMO / 10;

static {

System.out.println();

}

public static void main(String[] args) {

// for (int i = 0; i < 100; i++) { long[] c = new long[LEN_IN_THIS_DEMO]; // ~7.4GB System.out.println(c.length);

// } }

}

这里把题主的例子里slice长度给缩小到1/10,是因为Java语言规范规定数组长度一定要是int类型的,而题主例子里的长度超范围了。不过这不重要。循环也给注释掉了因为也不重要。

用GraalVM 0.22版(使用Graal作为JIT编译器的HotSpot VM)来跑这个测试,并且强制让main()一开始就被JIT编译好,会看到main()对应的机器码是这样的:

DemoEA.main (null) [0x000000010bc00da0, 0x000000010bc00e10] 112 bytes

[Entry Point]

[Verified Entry Point]

[Constants]

# {method} {0x000000011563fb58} 'main' '([Ljava/lang/String;)V' in 'DemoEA'

# parm0: rsi:rsi = '[Ljava/lang/String;'

# [sp+0x20] (sp of caller)

0x000000010bc00da0: mov %eax,-0x14000(%rsp)

0x000000010bc00da7: sub $0x18,%rsp

0x000000010bc00dab: mov %rbp,0x10(%rsp)

0x000000010bc00db0: movabs $0x1184420a0,%rsi ;*getstatic out {reexecute=0 rethrow=0 return_oop=0}

; - DemoEA::main@5 (line 12)

; {oop(a 'java/lang/Class' = 'java/lang/System')}

0x000000010bc00dba: mov 0xa8(%rsi),%rsi ; OopMap{rsi=Oop off=33}

;*ldc {reexecute=1 rethrow=0 return_oop=0}

; - DemoEA::main@0 (line 11)

0x000000010bc00dc1: test %eax,(%rsi)

0x000000010bc00dc3: mov $0x86796cc,%edx ;*invokevirtual println {reexecute=0 rethrow=0 return_oop=0}

; - DemoEA::main@10 (line 12)

0x000000010bc00dc8: nopl 0x0(%rax)

0x000000010bc00dcf: callq 0x000000010bba9020 ; OopMap{off=52}

;*invokevirtual println {reexecute=0 rethrow=0 return_oop=0}

; - DemoEA::main@10 (line 12)

; {optimized virtual_call}

0x000000010bc00dd4: nop ;*invokevirtual println {reexecute=0 rethrow=0 return_oop=0}

; - DemoEA::main@10 (line 12)

0x000000010bc00dd5: mov 0x10(%rsp),%rbp

0x000000010bc00dda: add $0x18,%rsp

0x000000010bc00dde: test %eax,-0x1cc0dde(%rip) # 0x0000000109f40006

; {poll_return}

0x000000010bc00de4: vzeroupper

0x000000010bc00de7: retq

用Java来表示就是:

public static void main(String[] args) {

System.out.println(1_000_000_000);

}

那个大数组的分配直接被消除了,所以它只要在语言规范允许的范围内有多大并不会影响实验结果——不会由于大数组分配而触发GC,是否放在循环里也无所谓。我这里实验把循环注释掉主要是为了让输出的机器码短一点,不然Graal会默认循环展开很多次,读着麻烦。

* 实验的命令是:

$ graalvm-0.22/bin/java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:-TieredCompilation -Xcomp -XX:CompileCommand=compileonly,DemoEA,main -XX:CompileCommand=dontinline,java/*,* -XX:+PrintCompilation -XX:-UseCompressedOops DemoEA

好玩不? ^_^

注:完全相同的实验在原装HotSpot VM上用C2作为JIT编译器则达不到这个效果。C2会通过常量传播把 System.out.println(c.length) 变成 System.out.println(1_000_000_000),但那个无用的数组分配还是给留着了…所以这里我换了个JIT编译器来演示。嘻嘻,反正选择多。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值