【问题标题】:Difference in memory between two code fragments两个代码片段之间的内存差异
【发布时间】:2018-05-07 13:22:22
【问题描述】:

Java 代码示例 1:

ArrayList<ArrayList<Integer>> outerList = new ArrayList<ArrayList<Integer>>();
for(i=0; i<5000; i++){  
    outerList.add(new ArrayList<Integer>());
    for(j=0; j<5000; j++){
        outerList.get(i).add(1);
    }
}

Java 代码示例 2:

ArrayList<ArrayList<Integer>> outerList_n = new ArrayList<ArrayList<Integer>>();
ArrayList<Integer> innerList_n = new ArrayList<Integer>();

for(j=0; j<5000; j++){
    innerList_n.add(1);
}   
for(i=0; i<5000; i++){  
    outerList_n.add(innerList_n);
}

说明

代码示例 1 每次都创建一个新的 arrayList 以添加到外部列表,尽管内部列表的每个条目始终为 1。

代码示例 2 创建一个所有条目为 1 的数组,然后在 for 循环中将其添加到 outerList。

问题

1) 它们的内存占用不同还是相同? 我用 jdk.nashorn.internal.ir.debug.ObjectSizeCalculator 来计算,结果如下:

outerList的大小:514907072

outerList_n的大小:130048

2) 我不明白为什么上面的内存不同。两种情况下的数据结构不一样吗?

3) javascript 中的情况是否相同?当我为上述两种情况尝试各自的 javascript 代码时,我在两种情况下都得到了相同的内存占用。

Javascript 代码:

function sizeof(object){

  // initialise the list of objects and size
  var objects = [object];
  var size    = 0;

  // loop over the objects
  for (var index = 0; index < objects.length; index ++){

    // determine the type of the object
    switch (typeof objects[index]){

      // the object is a boolean
      case 'boolean': size += 4; break;

      // the object is a number
      case 'number': size += 8; break;

      // the object is a string
      case 'string': size += 2 * objects[index].length; break;

      // the object is a generic object
      case 'object':

        // if the object is not an array, add the sizes of the keys
        if (Object.prototype.toString.call(objects[index]) != '[object Array]'){
          for (var key in objects[index]) size += 2 * key.length;
        }

        // loop over the keys
        for (var key in objects[index]){    
          // determine whether the value has already been processed
          var processed = false;
          for (var search = 0; search < objects.length; search ++){
            if (objects[search] === objects[index][key]){
              processed = true;
              break;
            }
          }    
          // queue the value to be processed if appropriate
          if (!processed) objects.push(objects[index][key]);    
        }    
    }    
  }    
  // return the calculated size
  return size;    
}

// TWO SEPARATE FOR LOOPS
var start = new Date().getTime();
var i,j ;
var inner_arr1 = [];
var outer_arr1 = [];
for (i = 0; i < 100; i++) {
    inner_arr1.push("abcdefghijklmnopqrstuvwxyz");
}
for (j = 0; j < 100; j++) {
    outer_arr1.push(inner_arr1);
}
var end = new Date().getTime();
print("size of outer_arr1: "+sizeof(outer_arr1));
print("time of outer_arr1 (in ms): "+(end-start))


// NESTED FOR LOOPS
var start = new Date().getTime();
var outer_arr2 = [];
for (j = 0; j < 100; j++) {
    var inner_arr2 = [];    
    for (i = 0; i < 100; i++) {
        inner_arr2.push("abcdefghijklmnopqrstuvwxyz");   
    }
    outer_arr2.push(inner_arr1);    
}
var end = new Date().getTime();
print("size of outer_arr2: "+sizeof(outer_arr2));
print("time of outer_arr2 (in ms): "+(end-start))


/*
COMMAND:
jjs findingSize.js

OUTPUT:
size of outer_arr1: 52
time of outer_arr1 (in ms): 45
size of outer_arr2: 52
time of outer_arr2 (in ms): 58
*/

【问题讨论】:

  • 两者都不同,代码示例 1 将运行 25000 次并添加 5000 个不必要的列表
  • 第一个样本会运行 5000^2 次,而第二个样本只会运行 10000 次
  • 我知道迭代次数。但这里的问题是关于记忆的。
  • 提示:在第二种情况下,您可以看到两个 new 运算符。第一个有几个?

标签: javascript java memory


【解决方案1】:

它们的内存占用不同还是相同?

不同。在第一种情况下,您将创建 5001 个 ArrayList 实例。在第二个中,您只创建了两个 ArrayList 实例

这两种情况的数据结构不一样吗?

两种情况下的数据结构是相同的——你有一个数组数组。唯一的区别是,在第二种情况下,您的所有内部数组都是相同的 - 它们共享公共内存,当您更改某行中的值时,所有行都会更改

【讨论】:

  • 那么,当我执行 ArrayList.add(x) 时,x 不是副本而是原始数组本身?
  • 是的,java 中的类是引用类型。每个赋值复制引用,但不复制数组本身。您的假设对 C++ 有效(当然,如果您不存储指向向量的指针),但不适用于 java
  • 我已经编辑了问题以将我的观察包含在 javascript 中。你能回答为什么javascript中的内存是一样的吗?和 C++ 一样吗?
  • 你确定你的 sizeof 函数是正确的吗?如果您的数组应该包含 100 个元素,它如何返回 52?
  • 我会检查的。谢谢你:)
【解决方案2】:
  1. 代码示例 1 : 它将创建 5000 个不同 Lists 填充 5000 倍的值 1,全部保存在 outerList : 5000+1 = 5001 个不同的列表

  2. 代码示例 2outerList 包含 5000 次 相同 列表(填充 5000 次值 1):1+1 = 2 个不同的列表

【讨论】:

    【解决方案3】:

    在我看来,评估是正确的。您的第一个代码示例有一个内部循环。这比第二个样本成倍增加。

    说白了,第二个样本会迭代第一个循环 5000 次,然后是第二个循环 5000 次。

    第一个示例将迭代内循环 5000 次,对于外循环的每个循环(另外 5000 次),您将获得 5000*5000 次外循环的迭代。get(i).add(1)。

    我希望这能回答你的问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-13
      • 1970-01-01
      • 2019-10-19
      相关资源
      最近更新 更多