zoukankan      html  css  js  c++  java
  • java中显示设置实例为null多余吗

    大家先看段代码:

     1 //启动参数设置:-Xms20m -Xmx20m -XX:SurvivorRatio=8 -Xmn10m
    2 public static void main(String[] args) {
    3 @SuppressWarnings("unused")
    4 byte[] b1,b2,b3,b4;
    5 long start = System.currentTimeMillis();
    6 int i = 1;
    7 while(i++ < 1000){
    8 b1 = new byte[1 * _1M];
    9 b2 = new byte[4 * _1M];
    10 b2 = null;//这行注释掉的话,运行时间会不会有很大的不同呢?
    11 b2 = new byte[4 * _1M];
    12 }
    13 System.out.println(System.currentTimeMillis() - start);
    14 }

    先把我本地测试的结果贴出来:

    有 b2=null 没有 b2=null
    5922 ms 14749 ms
    6187 ms 15077 ms
    6066 ms 16021 ms

    --------------------------------------------------------------------------------------

    从结果可以看出来,在显示调用引用为null时,运行相同的代码1000次,时间竟然相差两倍有余。为何?

    我们先把上面代码改成只运行一次,并设置参数打印GC详细日志:

    1 //启动参数设置:-Xms20m -Xmx20m -XX:SurvivorRatio=8 -Xmn10m -XX:+PrintGCDetails
    2 public static void main(String[] args) {
    3 @SuppressWarnings("unused")
    4 byte[] b1, b2, b3, b4;
    5 b1 = new byte[1 * _1M];
    6 b2 = new byte[4 * _1M];
    7 b2 = null;//先不注释运行,再把这行注释掉运行一次,比较两次的GC日志
    8 b2 = new byte[4 * _1M];
    9 }

    GC日志如下:

    结论:当显示设置实例为null时,GC后的内存容量和GC消耗的时间都是不同的。此时显示设置为null的话,GC后已占用的堆内存更小,消耗时间更短。

    至于为什么为这样,其实上面的GC详细日志内存的使用比例已经能说明问题,对于不是很清楚jvm内存模型的同学来说,还是看得不太清楚,下面我用简单的图讲解原因:

    先看下内存管理图(这张图截至阿里温少的PPT)

    至于详细的各内存块的含义,大家可以在网上找资料。针对这文章中的示例代码运行参数(-Xms20m -Xmx20m -XX:SurvivorRatio=8 -Xmn10m)设置,将会这样分配内存:

    Eden 8m
    s0 1m
    s1 1m
    tenured 10m

    我们来分别详细分析下上面两种情况下内存的详细分配:

    第一种情况(不显示设置null时)

    b1 = new byte[1 * _1M];

    当执行这条语句时在Eden区分配1m内存。

    b2 = new byte[4 * _1M];
    

    当执行这语句时再往Eden区分配4m内存。

    b2 = new byte[4 * _1M];
    

    当执行这条语句时,虚拟机想再从Eden区申请4m内存,发现不够了(8m - 1m - 4m = 3m),于是进行一次GC操作。由于Eden区的两个内存块(b1为1m,b2为4m)现在都还不是可回收对象,所以会复制到survivor区,但是survivor区才1m空间放不下,于是复制到Tenured区。然后将Eden区块全部清空回收,这样Eden区又有8m空间了,可以将刚才的b2 = new byte[4 * _1M]放到eden区。最终内存情况就是下面的:

    第二种情况(显示设置null时)

    b1 = new byte[1 * _1M];

    当执行这条语句时在Eden区分配1m内存。

    b2 = new byte[4 * _1M];

    当执行这语句时再往Eden区分配4m内存。

    b2 = null;
    

    将第二步分配的4m字节数组标志为无效对象(空引用,待回收)

    b2 = new byte[4 * _1M];
    

    重新申请4m空间,发现不够了(8m - 1m - 4m = 3m),于是进行一次GC操作。此时发现在Eden区中,b1指向的1m的字节数组为有效对象,而别一个4m的字节数组为无效对象(b2=null)。如是将1m字节数组内存复制到Tenured区中(只复制b1指向的实例)。然后将Eden区块全部清空回收,这样Eden区又有8m空间了,可以将刚才的b2 = new byte[4 * _1M]放到eden区。最终内存情况就是下面的:

    总结:

    我写这篇文章并不是想说明代码要显示设置无用对象为null还是不要设置。因为我这里举的例子中vm参数设置有点偏激,只是为了更好的说明问题,在生产中决不会设置内存20M,然后一个对象还占几M的空间这样的比例。

    在一般的情况下,内存1G,对象实例也很小时,这个GC的时间回收点问题不会像本文的问题那样明显。所以权当学习而也。

  • 相关阅读:
    [LINUX-05]Linux的进程线程及调度
    如何感性地理解EM算法?
    [LINUX-04]linux进程、调度、线程、进程上下文等几点理解
    centos定时删除log文件
    关于 Nginx 配置的一些疑惑, Nginx 根据cookie 进行rewrite
    oracle中如何创建表的自增ID(通过序列)
    Windows下PHP7/5.6以上版本 如何连接Oracle 12c,并使用PDO
    Tomcat不能访问ln -s软连接文件夹的前因后果
    rm命令删除文件时排除特定文件
    nginx中的url转发
  • 原文地址:https://www.cnblogs.com/jcli/p/2154276.html
Copyright © 2011-2022 走看看