在内存中确定Java列表大小的困难

时间:2018-04-15 22:40:27

标签: java memory memory-management runtime

在演示代码中发生了一些奇怪的事情我正在创建以说明Java ArrayList和LinkedList之间的权衡。问题:在创建大小为100000的ArrayList后,我在可用内存中观察到增加。我在这里缺少什么?

观察输出:

Total memory: 514850816 bytes   Free memory: 511852088 bytes    Used memory: 2998728 bytes
ArrayList random access: 8 ms
ArrayList rotation: 740 ms
Total memory: 514850816 bytes   Free memory: 512526416 bytes    Used memory: 2324400 bytes
Approximate ArrayList memory usage with 100000 4-byte integers: -674328 bytes (!?!?!?)
LinkedList random access: 2345 ms
LinkedList rotation: 5 ms
Total memory: 514850816 bytes   Free memory: 507136648 bytes    Used memory: 7714168 bytes
Approximate LinkedList memory usage with 100000 4-byte integers: 5389768 bytes
100000
100000
81838
46976

请注意,在使用ArrayList增加内存使用后,可用内存的差异为。用"(!?!?!?)"

查看上面的输出行

这是生成输出的代码:

public static void main(String[] args) throws InterruptedException {

    // An ArrayList is like a dynamically resizable array
    // - has a capacity that can be beyond size()  (trimToSize() for space efficiency)
    // - good for random access
    // - poor for insertion (must shift all elements to different indices)

    // A LinkedList is a doubly-linked list with links forward and backward between nodes
    // - poor for random access
    // - good for insertion

    System.gc(); // force system garbage collection
    long totalMemory = Runtime.getRuntime().totalMemory();
    long freeMemory = Runtime.getRuntime().freeMemory();
    long memoryBefore = totalMemory - freeMemory;
    System.out.println("Total memory: " + Runtime.getRuntime().totalMemory() + " bytes \tFree memory: " + Runtime.getRuntime().freeMemory()
            + " bytes \tUsed memory: " + memoryBefore + " bytes");

    final int SIZE = 100000;
    // ArrayList demo:
    ArrayList<Integer> aList = new ArrayList<>();
    for (int i = 0; i < SIZE; i++)
        aList.add(i);

    // Random access
    long startTime = System.currentTimeMillis();
    for (int i = 0; i < SIZE / 2; i++) {
        int value = aList.get((int) (SIZE * Math.random()));
        value = value + 1;
    }
    long endTime = System.currentTimeMillis();
    System.out.printf("ArrayList random access: %d ms\n", endTime - startTime);

    // Right rotate half of elements, one at a time
    startTime = System.currentTimeMillis();
    for (int i = 0; i < SIZE / 2; i++) {
        int value = aList.remove(aList.size() - 1);
        aList.add(0, value);
    }
    endTime = System.currentTimeMillis();
    System.out.printf("ArrayList rotation: %d ms\n", endTime - startTime);

    aList.trimToSize(); // ArrayLists are Object[] underneath that are copied to and replaced by arrays doubled in size as needed.
      // trimToSize() trims the array to its currently needed size
    System.gc(); // force system garbage collection
    totalMemory = Runtime.getRuntime().totalMemory();
    freeMemory = Runtime.getRuntime().freeMemory();
    long memoryAfterArrayList = totalMemory - freeMemory;
    System.out.println("Total memory: " + totalMemory + " bytes \tFree memory: " + freeMemory
            + " bytes \tUsed memory: " + memoryAfterArrayList + " bytes");
    System.out.println("Approximate ArrayList memory usage with " + SIZE + " 4-byte integers: " + (memoryAfterArrayList - memoryBefore) + " bytes (!?!?!?)");

    // LinkedList demo:

    LinkedList<Integer> lList = new LinkedList<>();
    for (int i = 0; i < SIZE; i++)
        lList.add(i);

    // Random access
    startTime = System.currentTimeMillis();
    for (int i = 0; i < SIZE / 2; i++) {
        int value = lList.get((int) (SIZE * Math.random()));
        value = value + 1;
    }
    endTime = System.currentTimeMillis();
    System.out.printf("LinkedList random access: %d ms\n", endTime - startTime);
    // NOTE: This is why you should use an iterator/for-each loop
    // for a LinkedList rather than a for loop with .get(index) operations.

    // Right rotate half of elements, one at a time
    startTime = System.currentTimeMillis();
    for (int i = 0; i < SIZE / 2; i++) {
        lList.addFirst(lList.removeLast());
    }
    endTime = System.currentTimeMillis();
    System.out.printf("LinkedList rotation: %d ms\n", endTime - startTime);

    totalMemory = Runtime.getRuntime().totalMemory();
    freeMemory = Runtime.getRuntime().freeMemory();
    long memoryAfterLinkedList = totalMemory - freeMemory;
    System.out.println("Total memory: " + totalMemory + " bytes \tFree memory: " + freeMemory
            + " bytes \tUsed memory: " + memoryAfterLinkedList + " bytes");
    System.out.println("Approximate LinkedList memory usage with " + SIZE + " 4-byte integers: " + (memoryAfterLinkedList - memoryAfterArrayList) + " bytes");

    // So both ArrayList and LinkedLists are AbstractLists, but with very
    // different implementations and very different performance for different tasks.
    // Conclusion: You should know _how_ data structures are implemented (CS 216)
    // to make proper application of them.

    // To prevent any optimizing compilation that would free aList or lList before this point:
    System.out.println(aList.size());
    System.out.println(lList.size());
    System.out.println(aList.get((int) (SIZE * Math.random())));
    System.out.println(lList.get((int) (SIZE * Math.random())));
}

此外,如果您有更好的方法来显示Java中数据结构的实时内存使用情况,我全力以赴。谢谢!

更新:根据Jorn Vornee的建议,我使用Java 1.9(Java(TM)SE运行时环境(build 9.0)从shell(而不是如上所述的Eclipse)中使用-Xlog:gc *选项运行它.1 + 11)),得到以下输出:

[0.019s][info][gc] Using G1
[0.088s][info][gc] GC(0) Pause Full (System.gc()) 1M->0M(8M) 6.102ms
Total memory: 8388608 bytes     Free memory: 7623384 bytes      Used memory: 765224 bytes
[0.095s][info][gc] GC(1) Pause Young (G1 Evacuation Pause) 2M->2M(8M) 1.963ms
ArrayList random access: 14 ms
[0.861s][info][gc] GC(2) Pause Young (G1 Evacuation Pause) 4M->2M(10M) 2.938ms
ArrayList rotation: 740 ms
[0.882s][info][gc] GC(3) Pause Full (System.gc()) 3M->2M(10M) 9.494ms
Total memory: 10485760 bytes    Free memory: 7602584 bytes      Used memory: 
2883176 bytes
Approximate ArrayList memory usage with 100000 4-byte integers: 2117952 bytes (!?!?!?)
[0.897s][info][gc] GC(4) Pause Young (G1 Evacuation Pause) 4M->4M(10M) 6.709ms
LinkedList random access: 2603 ms
[3.516s][info][gc] GC(5) Pause Initial Mark (G1 Evacuation Pause) 6M->6M(12M) 14.172ms
[3.516s][info][gc] GC(6) Concurrent Cycle
LinkedList rotation: 23 ms
Total memory: 12582912 bytes    Free memory: 4411232 bytes      Used memory: 
8171680 bytes
Approximate LinkedList memory usage with 100000 4-byte integers: 5288504 bytes
100000
100000
91956
67844
[3.536s][info][gc] GC(6) Pause Remark 7M->7M(12M) 2.713ms
[3.538s][info][gc] GC(6) Pause Cleanup 7M->7M(12M) 0.062ms
[3.539s][info][gc] GC(6) Concurrent Cycle 22.275ms

之前的运行是jre1.8.0_151。知道为什么这些Java版本之间的内存行为有如此显着的差异?此外,Jorn Vornee,相关帖子有相当多的推荐列表(谢谢!),但有没有特别推荐你认为最有益?

0 个答案:

没有答案