Java: оценки памяти структуры данных - PullRequest
5 голосов
/ 22 марта 2012

Есть ли у кого-нибудь приблизительные оценки для различных структур данных? например,

  • Массивы
  • Списки
  • HashMaps
  • LinkedLists

Я помню, как видел некоторые из этих оценок в разных местах, но сейчас я не могу их найти.

Я знаю, что на самом деле невероятно сложно , особенно для таких вещей, как HashMaps, но я ищу что-то действительно грубое, например:

Memory(HashMap) = fixedOverhead + variableOverhead * tableSize + A*numKeys + B*numValues + Memory(allKeys) + Memory(allValues)

конечно, это будет сильно варьироваться в зависимости от того и другого, но даже приблизительная оценка в пределах коэффициента 2 будет очень полезна.

Ответы [ 5 ]

3 голосов
2 голосов
/ 22 марта 2012

Эта таблица является довольно исчерпывающей и точно касается вариантов реализации JDK, измеряемых в байтах на элемент / элемент.Если вы хотите сделать это на своем компьютере - возможно, если вы работаете на другом компьютере - этот сайт Google Code позволит вам загрузить его исходный код.http://code.google.com/p/memory-measurer/wiki/ElementCostInDataStructures

0 голосов
/ 28 марта 2012

В Infoq, есть презентация infoq-11-nov-jvmperformance.mp3 от работника в твиттере: PDF-слайды, аудио: mp3 и видео.

В нем много говорится о коллекциях и других деталях размера объектов в JVM.

0 голосов
/ 22 марта 2012

Вот простая программа, которая просто потребляет оперативную память:

import java.util.*;
/**
    RamInit (c) GPLv3

    @author Stefan Wagner
    @date Do 22. Mär 08:40:40 CET 2012

*/
public class RamInit
{
    private java.lang.Object consumer; 

    public RamInit (char type, int size)
    {
        switch (type) 
        {
            case 'a': Integer [] ai = new Integer [size]; 
                for (int i = 0; i < size; ++i) 
                    ai[i] = i; 
                consumer = ai; 
                break;
            case 'l': List<Integer> li = new ArrayList<Integer> (); 
                for (int i = 0; i < size; ++i) 
                    li.add (i); 
                consumer = li;
                break;
            case 'h': HashMap <Integer, Integer> hm = new HashMap <Integer, Integer> (); 
                for (int i = 0; i < size; ++i) 
                    hm.put (i, size - i); 
                consumer = hm;
                break;
            case 'L': LinkedList <Integer> ll = new LinkedList <Integer> (); 
                for (int i = 0; i < size; ++i) 
                    ll.add (i);     
                consumer = ll;          
                break;
            default: System.err.println ("invalid: " + type);
        }
    }

    public static void main (String args[])
    {
        char type = 'a';
        int size = 1000000; // 1M
        if (args.length == 2)
        {
            type = args[0].charAt (0);
            size = Integer.parseInt (args[1]);
        }
        try {
            new RamInit (type, size);
        }
        catch (OutOfMemoryError oome)
        {
            System.exit (1);
        }
    }
}

А вот очень простой скрипт для его проверки:

#!/bin/bash

iterProg () {
ram=$1
maxram=$2 
typ=$3
size=$4
# echo java -Xmx${ram}M RamInit $typ $((size*1000*1000)) 
echo -n "." 
java -Xmx${ram}M RamInit $typ $((size*1000*1000)) && echo -en "\n"$typ $size ${ram}M || { 
    if (($ram==$maxram))
    then
        # echo "fail" 
        return 
    else 
        iterProg $((ram+1)) $maxram $typ $size 
    fi
    }
}

# try from 16 MB to 256
for typ in {a,l,h,L}; do 
  for size in {1,2,4}; do 
    iterProg $((size*17+1)) 256 $typ $size 
  done
done

Это примитивный итератор, и его следует заменить чем-то более сложным - например, если вам нужно 37 МБ для вызова RamInit с элементами Collection a и 1M, вам следует начать с элементов 2M с более чем этим.

И вы должны выбрать шаги в бинарном поиске, например, если 20M слишком мало, отметьте 128, затем (20 + 128) / 2, а затем среднее значение, в зависимости от успеха или неудачи с нижним пределом или верхний предел.

Поскольку HashMap хранит 2 Ints на элемент, он может начинаться примерно с двойного размера List / Array / Vector. Однако - время летит как стрелка, и во время записи результат закончен:

bash iterRamFind.sh 
..
a 1 19M.....
a 2 39M...............
a 4 83M..
l 1 19M.......
l 2 41M.......................
l 4 91M..............................................
h 1 63M.............................................................................................
h 2 127M...........................................................................................................................................................................................
h 4 255M......................
L 1 39M.................................................
L 2 83M...............................................................................................
L 4 163

Значение 17 объясняет себя из первых экспериментов. Как мы видим, размер увеличивается почти линейно.

Изменение кода для проверки влияния, которое вы используете Longs, зависит от вас - я думаю, вы закончите с коэффициентом 2 *.

0 голосов
/ 22 марта 2012

Это довольно грубо, но эти оценки должны быть правильными. Они предназначены для простых структур данных без учета переменных длины или каких-либо других дополнительных функций, которые обычно включаются в Java.

где dataType - тип хранимых данных

Array: (length n)
    n*sizeOf(dataType)

LinkedList:
    n*(sizeOf(dataType)+sizeOf(pointer))+sizeOf(pointer[head pointer])

List: 
    Array-backed=SpaceEfficiency(Array)
    LinkedList-backed=SpaceEfficiency(LinkedList)

HashMap: with v values, k keys
    v*sizeOf(valueDataType)

Tree: k-way tree with n nodes
    n*(sizeOf(nodeDataType)+(k*sizeOf(pointer)))+sizeOf(pointer[head pointer])

Graph: e edges, v vertices
    AdjacencyList:
        at most: v*((v*sizeOf(vertexDataType))+(e*sizeOf(pointer))) fully connected graph
        at least: v*sizeOf(vertexDataType) disconnected graph
    AdjacencyMatrix:
        v^2*sizeOf(int)
...