W Javie jaki jest najlepszy sposób na określenie rozmiaru obiektu?

Na przykład, załóżmy, że mam aplikację, która może czytać w pliku CSV ze stosami wierszy danych. Podaję użytkownikowi podsumowanie liczby wierszy na podstawie typów danych, ale chcę się upewnić, że nie odczytuję zbyt wielu wierszy danych i nie wywołuję OutOfMemoryError s. każdy wiersz przekłada się na obiekt. Czy istnieje łatwy sposób, aby programowo określić rozmiar tego obiektu? Czy istnieje odniesienie, które określa, jak duże są prymitywne typy i odniesienia do obiektów dla VM?

Prawo teraz, mam kod, który mówi odczyt do 32,000 wierszy, ale chciałbym również mieć kod, który mówi odczyt jak najwięcej wierszy, dopóki nie użyję 32MB pamięci. Może to inne pytanie, ale chciałbym wiedzieć.

 545
Author: Jeffrey Bosboom, 2008-09-09

24 answers

Możesz użyć Javy.lang.pakiet instrumentów

Skompiluj i umieść tę klasę w słoiku:

import java.lang.instrument.Instrumentation;

public class ObjectSizeFetcher {
    private static Instrumentation instrumentation;

    public static void premain(String args, Instrumentation inst) {
        instrumentation = inst;
    }

    public static long getObjectSize(Object o) {
        return instrumentation.getObjectSize(o);
    }
}

Dodaj do swojego MANIFEST.MF:

Premain-Class: ObjectSizeFetcher

Użyj getObjectSize:

public class C {
    private int x;
    private int y;

    public static void main(String [] args) {
        System.out.println(ObjectSizeFetcher.getObjectSize(new C()));
    }
}

Wywołanie z:

java -javaagent:ObjectSizeFetcherAgent.jar C
 417
Author: Stefan Karlsson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-28 18:24:59

Kilka lat temu Javaworld miał artykuł o określaniu rozmiaru złożonych i potencjalnie zagnieżdżonych obiektów Javy , w zasadzie przeszedł przez tworzenie implementacji sizeof() w Javie. Podejście opiera się zasadniczo na innych pracach, w których ludzie eksperymentalnie zidentyfikowali rozmiar prymitywów i typowych obiektów Javy, a następnie zastosowali tę wiedzę do metody, która rekurencyjnie przechodzi Wykres obiektu w celu obliczenia całkowitego rozmiaru.

Zawsze będzie nieco mniej dokładna niż natywna implementacja C po prostu ze względu na to, co dzieje się za kulisami klasy, ale powinna być dobrym wskaźnikiem.

Alternatywnie projekt SourceForge odpowiednio o nazwie sizeof oferujący bibliotekę Java5 z implementacją sizeof ().

P. S. nie używaj podejścia do serializacji, nie ma korelacji między wielkością serializowanego obiektu a ilością pamięci, którą zużywa podczas pracy.

 70
Author: Boris Terzic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-09 18:42:54

Powinieneś użyć jol , narzędzia opracowanego w ramach projektu OpenJDK.

Jol (Java Object Layout) to mały zestaw narzędzi do analizy schematów układów obiektów w JVMs. Narzędzia te wykorzystują w znacznym stopniu agenta ds. bezpieczeństwa, JVMTI i Serviceability (SA) do dekodowania rzeczywistego układu obiektu, footprintu i odniesień. To sprawia, że JOL jest znacznie dokładniejszy niż inne narzędzia opierające się na wysypiskach śmieci, założeniach specyfikacji itp.

Aby uzyskać rozmiary prymitywów, odwołania i elementy tablicy, użyj VMSupport.vmDetails(). W przypadku Oracle JDK 1.8.0_40 działającego na 64-bitowym systemie Windows (używane dla wszystkich poniższych przykładów) metoda ta zwraca

Running 64-bit HotSpot VM.
Using compressed oop with 0-bit shift.
Using compressed klass with 3-bit shift.
Objects are 8 bytes aligned.
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

Można uzyskać rozmiar Płytki instancji obiektu za pomocą ClassLayout.parseClass(Foo.class).toPrintable() (opcjonalnie przekazując instancję do toPrintable). Jest to tylko przestrzeń zajmowana przez pojedynczą instancję tej klasy; nie zawiera ona żadnych innych obiektów, do których odwołuje się ta klasa. To robi zawierają nadmiarowe maszyny wirtualne dla nagłówka obiektu, wyrównania pól i wypełnienia. Dla java.util.regex.Pattern:

java.util.regex.Pattern object internals:
 OFFSET  SIZE        TYPE DESCRIPTION                    VALUE
      0     4             (object header)                01 00 00 00 (0000 0001 0000 0000 0000 0000 0000 0000)
      4     4             (object header)                00 00 00 00 (0000 0000 0000 0000 0000 0000 0000 0000)
      8     4             (object header)                cb cf 00 20 (1100 1011 1100 1111 0000 0000 0010 0000)
     12     4         int Pattern.flags                  0
     16     4         int Pattern.capturingGroupCount    1
     20     4         int Pattern.localCount             0
     24     4         int Pattern.cursor                 48
     28     4         int Pattern.patternLength          0
     32     1     boolean Pattern.compiled               true
     33     1     boolean Pattern.hasSupplementary       false
     34     2             (alignment/padding gap)        N/A
     36     4      String Pattern.pattern                (object)
     40     4      String Pattern.normalizedPattern      (object)
     44     4        Node Pattern.root                   (object)
     48     4        Node Pattern.matchRoot              (object)
     52     4       int[] Pattern.buffer                 null
     56     4         Map Pattern.namedGroups            null
     60     4 GroupHead[] Pattern.groupNodes             null
     64     4       int[] Pattern.temp                   null
     68     4             (loss due to the next object alignment)
Instance size: 72 bytes (reported by Instrumentation API)
Space losses: 2 bytes internal + 4 bytes external = 6 bytes total

Można uzyskać widok sumaryczny głębokiego rozmiaru instancji obiektu za pomocą GraphLayout.parseInstance(obj).toFootprint(). Oczywiście, niektóre obiekty w footprint mogą być współdzielone (również odwołujące się do innych obiektów), więc jest to nadmierna redukcja przestrzeni, która może być odzyskana, gdy ten obiekt jest zbierany. Dla wyniku Pattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$") (pobranego z tej odpowiedzi), Jol zgłasza całkowity footprint 1840 bajtów, z czego tylko 72 są samą instancją wzorca.

java.util.regex.Pattern instance footprint:
     COUNT       AVG       SUM   DESCRIPTION
         1       112       112   [C
         3       272       816   [Z
         1        24        24   java.lang.String
         1        72        72   java.util.regex.Pattern
         9        24       216   java.util.regex.Pattern$1
        13        24       312   java.util.regex.Pattern$5
         1        16        16   java.util.regex.Pattern$Begin
         3        24        72   java.util.regex.Pattern$BitClass
         3        32        96   java.util.regex.Pattern$Curly
         1        24        24   java.util.regex.Pattern$Dollar
         1        16        16   java.util.regex.Pattern$LastNode
         1        16        16   java.util.regex.Pattern$Node
         2        24        48   java.util.regex.Pattern$Single
        40                1840   (total)

Jeśli zamiast tego użyj GraphLayout.parseInstance(obj).toPrintable(), jol powie Ci adres, rozmiar, Typ, wartość i ścieżkę dereferencji pól do każdego odwołanego obiektu, chociaż zwykle jest to zbyt szczegółowe, aby było przydatne. W trwającym przykładzie wzorca możesz uzyskać następujące informacje. (Adresy prawdopodobnie zmienią się między uruchomieniami.)

java.util.regex.Pattern object externals:
          ADDRESS       SIZE TYPE                             PATH                           VALUE
         d5e5f290         16 java.util.regex.Pattern$Node     .root.next.atom.next           (object)
         d5e5f2a0        120 (something else)                 (somewhere else)               (something else)
         d5e5f318         16 java.util.regex.Pattern$LastNode .root.next.next.next.next.next.next.next (object)
         d5e5f328      21664 (something else)                 (somewhere else)               (something else)
         d5e647c8         24 java.lang.String                 .pattern                       (object)
         d5e647e0        112 [C                               .pattern.value                 [^, [, a, -, z, A, -, Z, 0, -, 9, _, ., +, -, ], +, @, [, a, -, z, A, -, Z, 0, -, 9, -, ], +, \, ., [, a, -, z, A, -, Z, 0, -, 9, -, ., ], +, $]
         d5e64850        448 (something else)                 (somewhere else)               (something else)
         d5e64a10         72 java.util.regex.Pattern                                         (object)
         d5e64a58        416 (something else)                 (somewhere else)               (something else)
         d5e64bf8         16 java.util.regex.Pattern$Begin    .root                          (object)
         d5e64c08         24 java.util.regex.Pattern$BitClass .root.next.atom.val$rhs        (object)
         d5e64c20        272 [Z                               .root.next.atom.val$rhs.bits   [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e64d30         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64d48         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e64d60         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64d78         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e64d90         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64da8         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e64dc0         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs (object)
         d5e64dd8         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs        (object)
         d5e64df0         24 java.util.regex.Pattern$5        .root.next.atom                (object)
         d5e64e08         32 java.util.regex.Pattern$Curly    .root.next                     (object)
         d5e64e28         24 java.util.regex.Pattern$Single   .root.next.next                (object)
         d5e64e40         24 java.util.regex.Pattern$BitClass .root.next.next.next.atom.val$rhs (object)
         d5e64e58        272 [Z                               .root.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e64f68         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e64f80         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
         d5e64f98         24 java.util.regex.Pattern$5        .root.next.next.next.atom.val$lhs.val$lhs (object)
         d5e64fb0         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$rhs (object)
         d5e64fc8         24 java.util.regex.Pattern$5        .root.next.next.next.atom.val$lhs (object)
         d5e64fe0         24 java.util.regex.Pattern$5        .root.next.next.next.atom      (object)
         d5e64ff8         32 java.util.regex.Pattern$Curly    .root.next.next.next           (object)
         d5e65018         24 java.util.regex.Pattern$Single   .root.next.next.next.next      (object)
         d5e65030         24 java.util.regex.Pattern$BitClass .root.next.next.next.next.next.atom.val$rhs (object)
         d5e65048        272 [Z                               .root.next.next.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e65158         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e65170         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e65188         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e651a0         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
         d5e651b8         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs.val$lhs (object)
         d5e651d0         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs (object)
         d5e651e8         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom (object)
         d5e65200         32 java.util.regex.Pattern$Curly    .root.next.next.next.next.next (object)
         d5e65220        120 (something else)                 (somewhere else)               (something else)
         d5e65298         24 java.util.regex.Pattern$Dollar   .root.next.next.next.next.next.next (object)

Wpisy "(coś innego)" opisują inne obiekty w stercie, które nie są częścią tego wykresu obiektów .

Najlepsza dokumentacja jol to próbki jol w repozytorium jol. Próbki pokazują typowe operacje jol i pokazują, w jaki sposób można użyć jol do analizy wewnętrznych maszyn wirtualnych i garbage collector.

 63
Author: Jeffrey Bosboom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-04 00:46:52

Po Pierwsze "rozmiar obiektu" nie jest dobrze zdefiniowanym pojęciem w Javie. Możesz oznaczać sam obiekt, zawierający tylko jego elementy, obiekt i wszystkie obiekty, do których się odnosi (Wykres odniesienia). Możesz oznaczać rozmiar w pamięci lub rozmiar na dysku. A JVM może optymalizować takie rzeczy jak ciągi.

Więc jedynym poprawnym sposobem jest zapytać JVM, z dobrym profilerem (używam YourKit ), który prawdopodobnie nie jest tym, czego chcesz.

Jednak z powyższego opisu wynika, że wygląda na to, że każdy wiersz będzie samowystarczalny i nie będzie miał dużego drzewa zależności, więc metoda serializacji będzie prawdopodobnie dobrym przybliżeniem dla większości JVMs. Najprostszym sposobem na to jest:

 Serializable ser;
 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(baos);
 oos.writeObject(ser);
 oos.close();
 return baos.size();

Pamiętaj, że jeśli masz obiekty o wspólnych odniesieniach to nie da poprawnego wyniku, a rozmiar serializacji nie zawsze będzie pasował do rozmiaru w pamięci, ale jest to dobre przybliżenie. Kod będzie nieco bardziej wydajny, jeśli zainicjujesz Rozmiar ByteArrayOutputStream do wartości sensownej.

 56
Author: Nick Fortescue,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-03 15:52:59

Przypadkowo znalazłem klasę Javy "jdk.Nashorn.internal.ir. debug. ObjectSizeCalculator", już w jdk, który jest łatwy w użyciu i wydaje się bardzo przydatny do określania wielkości obiektu.

System.out.println(ObjectSizeCalculator.getObjectSize(new gnu.trove.map.hash.TObjectIntHashMap<String>(12000, 0.6f, -1)));
System.out.println(ObjectSizeCalculator.getObjectSize(new HashMap<String, Integer>(100000)));
System.out.println(ObjectSizeCalculator.getObjectSize(3));
System.out.println(ObjectSizeCalculator.getObjectSize(new int[]{1, 2, 3, 4, 5, 6, 7 }));
System.out.println(ObjectSizeCalculator.getObjectSize(new int[100]));

Wyniki:

164192
48
16
48
416
 44
Author: Tom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-09 07:53:20

Jeśli chcesz wiedzieć, ile pamięci jest używane w Twoim JVM i ile jest za darmo, możesz spróbować czegoś takiego:

// Get current size of heap in bytes
long heapSize = Runtime.getRuntime().totalMemory();

// Get maximum size of heap in bytes. The heap cannot grow beyond this size.
// Any attempt will result in an OutOfMemoryException.
long heapMaxSize = Runtime.getRuntime().maxMemory();

// Get amount of free memory within the heap in bytes. This size will increase
// after garbage collection and decrease as new objects are created.
long heapFreeSize = Runtime.getRuntime().freeMemory();

Edit: pomyślałem, że to może być pomocne, ponieważ autor pytania stwierdził również, że chciałby mieć logikę, która obsługuje " czytać jak najwięcej wierszy, dopóki nie użyję 32MB pamięci."

 33
Author: matt b,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-16 16:08:47

Kiedy pracowałem na Twitterze, napisałem narzędzie do obliczania rozmiaru głębokiego obiektu. Uwzględnia różne modele pamięci (32-bitowe, skompresowane, 64-bitowe), padding, padding podklasowy, działa poprawnie na okrągłych strukturach danych i tablicach. Możesz po prostu skompilować ten .plik Javy; nie ma zewnętrznych zależności:

Https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/objectsize/ObjectSizeCalculator.java

 18
Author: Attila Szegedi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-09 11:07:01

Wiele innych odpowiedzi zapewnia płytkie rozmiary - np. rozmiar Hashmapy bez żadnych kluczy lub wartości, co prawdopodobnie nie jest tym, czego chcesz.

Projekt jamm używa Javy.lang.pakiet oprzyrządowania powyżej, ale chodzi po drzewie, a więc może dać ci Głębokie wykorzystanie pamięci.

new MemoryMeter().measureDeep(myHashMap);

Https://github.com/jbellis/jamm

 13
Author: rich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-06 14:07:34

Musisz chodzić po przedmiotach używając odbicia. Uważaj na siebie:

  • samo przydzielanie obiektu ma trochę narzutu w JVM. Kwota różni się w zależności od JVM, więc możesz ustawić tę wartość jako parametr. Przynajmniej niech będzie stała (8 bajtów?) i stosuje się do wszystkich przydzielonych.
  • to, że byte jest teoretycznie 1 bajtem, nie oznacza, że zajmuje tylko jeden bajt w pamięci.
  • w odniesieniach do obiektów będą pętle, więc będziesz musiał zachować HashMap lub somesuch używając object-equals jako komparator eliminujący nieskończone pętle.

@ jodonnell: podoba mi się prostota Twojego rozwiązania, ale wiele obiektów nie jest Serializowalnych (więc rzuciłoby to wyjątek), pola mogą być przejściowe, a obiekty mogą nadpisywać standardowe metody.

 10
Author: Jason Cohen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-09 17:19:48

Musisz zmierzyć go za pomocą narzędzia lub oszacować ręcznie i zależy to od JVM, którego używasz.

Istnieje pewne stałe narzuty na obiekt. Jest specyficzny dla JVM, ale zwykle szacuję 40 bajtów. Więc musisz spojrzeć na członków klasy. Odniesienia do obiektów mają 4 (8) bajtów w 32-bitowym (64-bitowym) JVM. Typy prymitywne to:

  • logiczny i Bajt: 1 bajt
  • char i short: 2 bajty
  • int i float: 4 bajty
  • długie i podwójne: 8 bajty

Tablice kierują się tymi samymi zasadami; to znaczy, że jest to odniesienie do obiektu, które zajmuje 4 (lub 8) bajtów w obiekcie, a następnie jego długość pomnożoną przez rozmiar jego elementu.

Próba zrobienia tego programowo z wywołaniami do Runtime.freeMemory() po prostu nie daje dużej dokładności, ze względu na asynchroniczne wywołania do garbage collector, itp. Profilowanie sterty za pomocą-Xrunhprof lub innych narzędzi da Ci najbardziej dokładne wyniki.

 8
Author: erickson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-26 04:04:16

Polecam bibliotekę java-sizeof dla carrotsearch. To bardzo proste.

Można go dostać w maven:

 <dependency>
    <groupId>com.carrotsearch</groupId>
    <artifactId>java-sizeof</artifactId>
    <version>0.0.3</version>
</dependency>

Jest to tylko jedna linia kodu, która zwraca bajty obiektu:

RamUsageEstimator.sizeOf(new Object());

Możesz zobaczyć kod źródłowy na https://github.com/dweiss/java-sizeof

I jest prezentacja autora biblioteki http://www.slideshare.net/DawidWeiss/sizeofobject-how-much-memory-objects-take-on-jvms-and-when-this-may-matter?ref=http://cheremin.blogspot.com/2012/05/how-much-memory-objects-take-on-jvm-and.html

 7
Author: Rony Bichler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-23 12:14:17

Klasa java.lang.instrument.Instrumentation zapewnia dobry sposób na uzyskanie rozmiaru obiektu Java, ale wymaga zdefiniowania premain i uruchomienia programu za pomocą agenta java. Jest to bardzo nudne, gdy nie potrzebujesz żadnego agenta, a następnie musisz DOSTARCZYĆ atrapy agenta słoika do aplikacji.

Więc mam alternatywne rozwiązanie używając klasy Unsafe z sun.misc. Biorąc więc pod uwagę wyrównanie sterty obiektów zgodnie z architekturą procesora i obliczając maksymalne przesunięcie pola, można zmierzyć rozmiar obiektu Java. W poniższym przykładzie używam klasy pomocniczej UtilUnsafe, aby uzyskać odniesienie do obiektu sun.misc.Unsafe.

private static final int NR_BITS = Integer.valueOf(System.getProperty("sun.arch.data.model"));
private static final int BYTE = 8;
private static final int WORD = NR_BITS/BYTE;
private static final int MIN_SIZE = 16; 

public static int sizeOf(Class src){
    //
    // Get the instance fields of src class
    // 
    List<Field> instanceFields = new LinkedList<Field>();
    do{
        if(src == Object.class) return MIN_SIZE;
        for (Field f : src.getDeclaredFields()) {
            if((f.getModifiers() & Modifier.STATIC) == 0){
                instanceFields.add(f);
            }
        }
        src = src.getSuperclass();
    }while(instanceFields.isEmpty());
    //
    // Get the field with the maximum offset
    //  
    long maxOffset = 0;
    for (Field f : instanceFields) {
        long offset = UtilUnsafe.UNSAFE.objectFieldOffset(f);
        if(offset > maxOffset) maxOffset = offset; 
    }
    return  (((int)maxOffset/WORD) + 1)*WORD; 
}
class UtilUnsafe {
    public static final sun.misc.Unsafe UNSAFE;

    static {
        Object theUnsafe = null;
        Exception exception = null;
        try {
            Class<?> uc = Class.forName("sun.misc.Unsafe");
            Field f = uc.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            theUnsafe = f.get(uc);
        } catch (Exception e) { exception = e; }
        UNSAFE = (sun.misc.Unsafe) theUnsafe;
        if (UNSAFE == null) throw new Error("Could not obtain access to sun.misc.Unsafe", exception);
    }
    private UtilUnsafe() { }
}
 6
Author: Miguel Gamboa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-14 16:37:13

Istnieje również narzędzie Memory Measurer (dawniej na Google Code , Obecnie na GitHub ), które jest proste i opublikowane na komercyjnej licencji Apache 2.0, jak omówiono w podobne pytanie.

To również wymaga argumentu wiersza poleceń do interpretera Javy, jeśli chcesz zmierzyć zużycie bajtów pamięci, ale poza tym wydaje się działać dobrze, przynajmniej w scenariuszach, z których korzystałem.

 6
Author: PNS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:36

Oto narzędzie, które zrobiłem używając niektórych z połączonych przykładów do obsługi 32-bitowych, 64-bitowych i 64-bitowych ze skompresowanym OOP. Używa sun.misc.Unsafe.

Używa Unsafe.addressSize(), aby uzyskać rozmiar natywnego wskaźnika i Unsafe.arrayIndexScale( Object[].class ), aby uzyskać Rozmiar Odniesienia w Javie.

Używa offsetu pola znanej klasy do obliczenia bazowego rozmiaru obiektu.

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.IdentityHashMap;
import java.util.Stack;
import sun.misc.Unsafe;

/** Usage: 
 * MemoryUtil.sizeOf( object )
 * MemoryUtil.deepSizeOf( object )
 * MemoryUtil.ADDRESS_MODE
 */
public class MemoryUtil
{
    private MemoryUtil()
    {
    }

    public static enum AddressMode
    {
        /** Unknown address mode. Size calculations may be unreliable. */
        UNKNOWN,
        /** 32-bit address mode using 32-bit references. */
        MEM_32BIT,
        /** 64-bit address mode using 64-bit references. */
        MEM_64BIT,
        /** 64-bit address mode using 32-bit compressed references. */
        MEM_64BIT_COMPRESSED_OOPS
    }

    /** The detected runtime address mode. */
    public static final AddressMode ADDRESS_MODE;

    private static final Unsafe UNSAFE;

    private static final long ADDRESS_SIZE; // The size in bytes of a native pointer: 4 for 32 bit, 8 for 64 bit
    private static final long REFERENCE_SIZE; // The size of a Java reference: 4 for 32 bit, 4 for 64 bit compressed oops, 8 for 64 bit
    private static final long OBJECT_BASE_SIZE; // The minimum size of an Object: 8 for 32 bit, 12 for 64 bit compressed oops, 16 for 64 bit
    private static final long OBJECT_ALIGNMENT = 8;

    /** Use the offset of a known field to determine the minimum size of an object. */
    private static final Object HELPER_OBJECT = new Object() { byte b; };


    static
    {
        try
        {
            // Use reflection to get a reference to the 'Unsafe' object.
            Field f = Unsafe.class.getDeclaredField( "theUnsafe" );
            f.setAccessible( true );
            UNSAFE = (Unsafe) f.get( null );

            OBJECT_BASE_SIZE = UNSAFE.objectFieldOffset( HELPER_OBJECT.getClass().getDeclaredField( "b" ) );

            ADDRESS_SIZE = UNSAFE.addressSize();
            REFERENCE_SIZE = UNSAFE.arrayIndexScale( Object[].class );

            if( ADDRESS_SIZE == 4 )
            {
                ADDRESS_MODE = AddressMode.MEM_32BIT;
            }
            else if( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 8 )
            {
                ADDRESS_MODE = AddressMode.MEM_64BIT;
            }
            else if( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 4 )
            {
                ADDRESS_MODE = AddressMode.MEM_64BIT_COMPRESSED_OOPS;
            }
            else
            {
                ADDRESS_MODE = AddressMode.UNKNOWN;
            }
        }
        catch( Exception e )
        {
            throw new Error( e );
        }
    }


    /** Return the size of the object excluding any referenced objects. */
    public static long shallowSizeOf( final Object object )
    {
        Class<?> objectClass = object.getClass();
        if( objectClass.isArray() )
        {
            // Array size is base offset + length * element size
            long size = UNSAFE.arrayBaseOffset( objectClass )
                    + UNSAFE.arrayIndexScale( objectClass ) * Array.getLength( object );
            return padSize( size );
        }
        else
        {
            // Object size is the largest field offset padded out to 8 bytes
            long size = OBJECT_BASE_SIZE;
            do
            {
                for( Field field : objectClass.getDeclaredFields() )
                {
                    if( (field.getModifiers() & Modifier.STATIC) == 0 )
                    {
                        long offset = UNSAFE.objectFieldOffset( field );
                        if( offset >= size )
                        {
                            size = offset + 1; // Field size is between 1 and PAD_SIZE bytes. Padding will round up to padding size.
                        }
                    }
                }
                objectClass = objectClass.getSuperclass();
            }
            while( objectClass != null );

            return padSize( size );
        }
    }


    private static final long padSize( final long size )
    {
        return (size + (OBJECT_ALIGNMENT - 1)) & ~(OBJECT_ALIGNMENT - 1);
    }


    /** Return the size of the object including any referenced objects. */
    public static long deepSizeOf( final Object object )
    {
        IdentityHashMap<Object,Object> visited = new IdentityHashMap<Object,Object>();
        Stack<Object> stack = new Stack<Object>();
        if( object != null ) stack.push( object );

        long size = 0;
        while( !stack.isEmpty() )
        {
            size += internalSizeOf( stack.pop(), stack, visited );
        }
        return size;
    }


    private static long internalSizeOf( final Object object, final Stack<Object> stack, final IdentityHashMap<Object,Object> visited )
    {
        // Scan for object references and add to stack
        Class<?> c = object.getClass();
        if( c.isArray() && !c.getComponentType().isPrimitive() )
        {
            // Add unseen array elements to stack
            for( int i = Array.getLength( object ) - 1; i >= 0; i-- )
            {
                Object val = Array.get( object, i );
                if( val != null && visited.put( val, val ) == null )
                {
                    stack.add( val );
                }
            }
        }
        else
        {
            // Add unseen object references to the stack
            for( ; c != null; c = c.getSuperclass() )
            {
                for( Field field : c.getDeclaredFields() )
                {
                    if( (field.getModifiers() & Modifier.STATIC) == 0 
                            && !field.getType().isPrimitive() )
                    {
                        field.setAccessible( true );
                        try
                        {
                            Object val = field.get( object );
                            if( val != null && visited.put( val, val ) == null )
                            {
                                stack.add( val );
                            }
                        }
                        catch( IllegalArgumentException e )
                        {
                            throw new RuntimeException( e );
                        }
                        catch( IllegalAccessException e )
                        {
                            throw new RuntimeException( e );
                        }
                    }
                }
            }
        }

        return shallowSizeOf( object );
    }
}
 3
Author: dlaudams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-26 06:02:35

Bez bałaganu z oprzyrządowaniem i tak dalej, a jeśli nie musisz znać dokładnej wielkości bajtów obiektu, możesz zastosować następujące podejście:

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

do your job here

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

W ten sposób odczytujesz używaną pamięć przed i po, a wywołując GC tuż przed pobraniem używanej pamięci zmniejszasz "szum" prawie do 0.

Aby uzyskać bardziej wiarygodny wynik, można uruchomić pracę n razy, a następnie podzielić używaną pamięć przez n, uzyskując ile pamięci zajmuje jeden bieg. Co więcej, możesz biegać to wszystko więcej razy i zrobić średnią.

 3
Author: reallynice,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-03 12:09:59

Nie ma wywołania metody, jeśli o to prosisz. Z odrobiną badań, przypuszczam, że mógłbyś napisać swój własny. Konkretna instancja ma stały rozmiar na podstawie liczby odwołań i prymitywnych wartości oraz danych księgowych instancji. Wystarczy przejść po wykresie obiektu. Im mniej zróżnicowane typy rzędów, tym łatwiej.

Jeśli to zbyt wolne lub po prostu więcej kłopotów niż jest warte, zawsze jest dobra staromodna zasada liczenia wierszy kciuków.

 2
Author: sblundy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-09 17:15:40

Napisałem kiedyś szybki test do oszacowania w locie:

public class Test1 {

    // non-static nested
    class Nested { }

    // static nested
    static class StaticNested { }

    static long getFreeMemory () {
        // waits for free memory measurement to stabilize
        long init = Runtime.getRuntime().freeMemory(), init2;
        int count = 0;
        do {
            System.out.println("waiting..." + init);
            System.gc();
            try { Thread.sleep(250); } catch (Exception x) { }
            init2 = init;
            init = Runtime.getRuntime().freeMemory();
            if (init == init2) ++ count; else count = 0;
        } while (count < 5);
        System.out.println("ok..." + init);
        return init;
    }

    Test1 () throws InterruptedException {

        Object[] s = new Object[10000];
        Object[] n = new Object[10000];
        Object[] t = new Object[10000];

        long init = getFreeMemory();

        //for (int j = 0; j < 10000; ++ j)
        //    s[j] = new Separate();

        long afters = getFreeMemory();

        for (int j = 0; j < 10000; ++ j)
            n[j] = new Nested();

        long aftersn = getFreeMemory();

        for (int j = 0; j < 10000; ++ j)
            t[j] = new StaticNested();

        long aftersnt = getFreeMemory();

        System.out.println("separate:      " + -(afters - init) + " each=" + -(afters - init) / 10000);
        System.out.println("nested:        " + -(aftersn - afters) + " each=" + -(aftersn - afters) / 10000);
        System.out.println("static nested: " + -(aftersnt - aftersn) + " each=" + -(aftersnt - aftersn) / 10000);

    }

    public static void main (String[] args) throws InterruptedException {
        new Test1();
    }

}

Ogólna koncepcja to przydzielanie obiektów i mierzenie zmian w wolnej przestrzeni sterty. Kluczem jest getFreeMemory(), który żąda uruchomienia GC i oczekuje na stabilizację zgłoszonego rozmiaru sterty wolnej . Wynikiem powyższego jest:

nested:        160000 each=16
static nested: 160000 each=16

Tego właśnie oczekujemy, biorąc pod uwagę zachowanie wyrównania i możliwe nagłówki bloków sterty.

Metoda oprzyrządowania wyszczególniona w zaakceptowanej odpowiedzi tutaj najdokładniejsza. Na metoda, którą opisałem jest dokładna, ale tylko w kontrolowanych warunkach, gdzie żadne inne wątki nie tworzą / odrzucają obiektów.

 2
Author: Jason C,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-16 03:34:24

Wystarczy użyć Java visual VM.

Ma wszystko, czego potrzebujesz do profilowania i debugowania problemów z pamięcią.

Posiada również konsolę OQL (Object Query Language), która pozwala na wykonywanie wielu przydatnych rzeczy, z których jedną jest sizeof(o)

 2
Author: ACV,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-23 11:53:26

Możesz wygenerować zrzut sterty (na przykład za pomocą jmap), a następnie przeanalizować wyjście, aby znaleźć rozmiary obiektów. Jest to rozwiązanie offline, ale można zbadać płytkie i głębokie rozmiary itp.

 0
Author: JZeeb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-04 15:08:07
long heapSizeBefore = Runtime.getRuntime().totalMemory();

// Code for object construction
...
long heapSizeAfter = Runtime.getRuntime().totalMemory();
long size = heapSizeAfter - heapSizeBefore;

Size daje wzrost wykorzystania pamięci jvm z powodu tworzenia obiektu i zazwyczaj jest to rozmiar obiektu.

 0
Author: user835199,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-07 07:23:43

Moja odpowiedź opiera się na kodzie dostarczonym przez Nicka. Kod ten mierzy całkowitą ilość bajtów zajętych przez serializowany obiekt. Więc to faktycznie mierzy serializację rzeczy + zwykły obiekt pamięci footprintu(wystarczy serialize na przykład {[1] } i zobaczysz, że całkowita ilość serializowanych bajtów nie jest 4). Więc jeśli chcesz, aby surowy numer bajtu został użyty dokładnie dla Twojego obiektu - musisz trochę zmodyfikować ten kod. Tak:

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class ObjectSizeCalculator {
    private Object getFirstObjectReference(Object o) {
        String objectType = o.getClass().getTypeName();

        if (objectType.substring(objectType.length()-2).equals("[]")) {
            try {
                if (objectType.equals("java.lang.Object[]"))
                    return ((Object[])o)[0];
                else if (objectType.equals("int[]"))
                    return ((int[])o)[0];
                else
                    throw new RuntimeException("Not Implemented !");
            } catch (IndexOutOfBoundsException e) {
                return null;
            }
        }

        return o;
    } 

    public int getObjectSizeInBytes(Object o) {
        final String STRING_JAVA_TYPE_NAME = "java.lang.String";

        if (o == null)
            return 0;

        String objectType = o.getClass().getTypeName();
        boolean isArray = objectType.substring(objectType.length()-2).equals("[]");

        Object objRef = getFirstObjectReference(o);
        if (objRef != null && !(objRef instanceof Serializable))
            throw new RuntimeException("Object must be serializable for measuring it's memory footprint using this method !");

        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(o);
            oos.close();
            byte[] bytes = baos.toByteArray();

            for (int i = bytes.length - 1, j = 0; i != 0; i--, j++) {
                if (objectType != STRING_JAVA_TYPE_NAME) {
                    if (bytes[i] == 112)
                        if (isArray)
                            return j - 4;
                        else
                            return j;
                } else {
                    if (bytes[i] == 0)
                        return j - 1;
                }
            }
        } catch (Exception e) {
            return -1;
        }

        return -1;
    }    

}

Przetestowałem To rozwiązanie z prymitywnym types, String, a na niektórych trywialnych klasach. Nie mogą być objęte również przypadki.


UPDATE: przykład zmodyfikowany w celu obsługi obliczeń footprintów pamięci obiektów tablicy.

 0
Author: Agnius Vasiliauskas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-19 21:53:42

Ta odpowiedź nie jest związana z rozmiarem obiektu, ale kiedy używasz tablicy, aby pomieścić obiekty; ile pamięci zostanie przydzielone dla obiektu.

Tak więc tablice, lista, czy mapowanie wszystkich tych kolekcji nie będą tak naprawdę przechowywać obiektów (tylko w czasie pierwowzorów wymagany jest rzeczywisty rozmiar pamięci obiektu), będzie ona przechowywać tylko odwołania do tych obiektów.

Teraz Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection

  • (4/8 bajtów) zależy od (32/64 bit) OS

Prymitywy

int   [] intArray    = new int   [1]; will require 4 bytes.
long  [] longArray   = new long  [1]; will require 8 bytes.

Obiekty

Object[] objectArray = new Object[1]; will require 4 bytes. The object can be any user defined Object.
Long  [] longArray   = new Long  [1]; will require 4 bytes.

Chodzi mi o to, że wszystkie odniesienia do obiektu wymagają tylko 4 bajtów pamięci. Może to być String reference lub Double object reference, ale w zależności od utworzenia obiektu Wymagana pamięć będzie różna.

Np. jeśli stworzę obiekt dla poniższej klasy ReferenceMemoryTest to 4 + 4 + 4 = zostanie wytworzonych 12 bajtów pamięci. Pamięć może się różnić, gdy próbujesz zainicjować odwołania.

 class ReferenceMemoryTest {
    public String refStr;
    public Object refObj;
    public Double refDoub; 
}

Więc kiedy tworzy obiekt / tablicę referencyjną, cała jej zawartość będzie zajęta referencjami NULL. Wiemy, że każda Referencja wymaga 4 bajtów.

I wreszcie, alokacja pamięci dla poniższego kodu wynosi 20 bajtów.

ReferenceMemoryTest ref1 = new ReferenceMemoryTest (); (4 (ref1) + 12 = 16 bajtów) ReferenceMemoryTest ref2 = ref1; (4 (ref2) + 16 = 20 bajtów)

 0
Author: Kanagavelu Sugumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-13 03:32:51

Dla JSONObject poniższy kod może Ci pomóc.

`JSONObject.toString().getBytes("UTF-8").length`

Zwraca rozmiar w bajtach

Sprawdziłem to za pomocą mojego obiektu JSONArray, zapisując go do pliku. To daje rozmiar obiektu.

 -3
Author: solokiran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-01 15:56:11

Wątpię, czy chcesz to zrobić programowo, chyba że chcesz to zrobić raz i przechowywać do przyszłego użytku. To kosztowna rzecz. W Javie nie ma operatora sizeof (), a nawet gdyby tak było, policzyłby tylko koszt odwołań do innych obiektów i rozmiar prymitywów.

Jednym ze sposobów, aby to zrobić, jest serializacja rzeczy do pliku i spojrzeć na rozmiar pliku, Jak to:

Serializable myObject;
ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("obj.ser"));
oos.write (myObject);
oos.close ();

Oczywiście zakłada się, że każdy obiekt jest odrębny i nie zawierają nietrwałe odniesienia do czegokolwiek innego.

Inną strategią byłoby wzięcie każdego obiektu i zbadanie jego członków przez refleksję i dodanie rozmiarów (boolean & byte = 1 bajt, short & char = 2 bajty, itp.), wchodząc w hierarchię członkowską. Ale to jest żmudne i kosztowne i kończy się robiąc to samo strategia serializacji zrobi.

 -6
Author: jodonnell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-09 17:11:35