Vlákno názorů k článku Pohled pod kapotu JVM - jak efektivně jsou uložena pole a řetězce na haldě? od atarist - Tak v Jave je to ukladani kratkych retezcu...

  • Článek je starý, nové názory již nelze přidávat.
  • 16. 1. 2013 10:08

    atarist (neregistrovaný)

    Tak v Jave je to ukladani kratkych retezcu skutecne dost nevyhodne, jak to zvetsuje naroky cele JVM v beznych aplikacich? (jako asi je jedno, jestli je to o nejaky 1-2 MB vic pro normalni aplikaci , ale to asi budou jina cisla...)

    A jak je na tom Python - vzpominam si, ze nedavno tam byly nejaky problemy prave u kratkych retezcu a GC.

  • 16. 1. 2013 19:31

    Pavel Tišnovský
    Zlatý podporovatel

    Python interně pravděpodobně používá UTF-8:

    >>> import sys
    >>> sys.getsizeof("")
    24
    >>> sys.getsizeof(" ")
    25
    >>> sys.getsizeof("a")
    25
    >>> sys.getsizeof("ě")
    26
    >>> sys.getsizeof("ěščřžýáíé")
    42
    >>> sys.getsizeof("abcdefghi")
    33
    >>>

    Overhead tam je taky docela slušný, i když Java "vítězí" :-(

  • 16. 1. 2013 21:19

    kvr kvr

    Jestli dobře vidím, tak na 64bit je prázdný string 37 bytes (asi + terminating zero pro lepší interoperabilitu s C).
    Takže ve srovnání Java na tom tak špatně není, aspoň co se týče hlavní struktury (*). Navíc je otázka, zda potřebuje Java ještě overhead pro memory blok, Python tam určitě počítá pouze velikost samotné struktury.

    * - v constant pool má každý objekt vlastní pole nebo jej může sdílet víc stringů? Tím by se ta neefektivita zmenšila výrazně...

  • 17. 1. 2013 11:54

    Pavel Tišnovský
    Zlatý podporovatel

    Ano, ted jsem to zkousel na 64bitovym stroji (Python 2.7.3) a tam je pro prazdny string 37 bajtu a pro ten ěščřžýáíé 55 bajtu

  • 17. 1. 2013 12:00

    Pavel Tišnovský
    Zlatý podporovatel

    Ad sdílení stringů, to v Jave v ramci jedne tridy skutecne funguje, muzeme si to ostatne vyzkouset na tride, kde se vyskytuje stejny konstantni retezec (literal) na ruznem miste:

    public class Test {
        String s1 = "Hello world!";
        String s2 = "Hello world!";
        public void foo(String s) {
            System.out.println("Hello world!");
            System.out.println(s);
        }
        public void bar() {
            foo("Hello world!");
        }
    }

    javap -verbose Test vyplivne mj. i constant pool a z disasemblovaneho kodu je videt, ze se vsude pouziva odkaz na jediny zaznam #2:

    Compiled from "Test.java"
    public class Test extends java.lang.Object
      SourceFile: "Test.java"
      minor version: 0
      major version: 50
      Constant pool:
    const #1 = Method   #9.#22; //  java/lang/Object."[init]":()V
    const #2 = String   #23;    //  Hello world!
    const #3 = Field    #8.#24; //  Test.s1:Ljava/lang/String;
    const #4 = Field    #8.#25; //  Test.s2:Ljava/lang/String;
    const #5 = Field    #26.#27;    //  java/lang/System.out:Ljava/io/PrintStream;
    const #6 = Method   #28.#29;    //  java/io/PrintStream.println:(Ljava/lang/String;)V
    const #7 = Method   #8.#30; //  Test.foo:(Ljava/lang/String;)V
    const #8 = class    #31;    //  Test
    const #9 = class    #32;    //  java/lang/Object
    const #10 = Asciz   s1;
    const #11 = Asciz   Ljava/lang/String;;
    const #12 = Asciz   s2;
    const #13 = Asciz   [init];
    const #14 = Asciz   ()V;
    const #15 = Asciz   Code;
    const #16 = Asciz   LineNumberTable;
    const #17 = Asciz   foo;
    const #18 = Asciz   (Ljava/lang/String;)V;
    const #19 = Asciz   bar;
    const #20 = Asciz   SourceFile;
    const #21 = Asciz   Test.java;
    const #22 = NameAndType #13:#14;//  "[init]":()V
    const #23 = Asciz   Hello world!;
    const #24 = NameAndType #10:#11;//  s1:Ljava/lang/String;
    const #25 = NameAndType #12:#11;//  s2:Ljava/lang/String;
    const #26 = class   #33;    //  java/lang/System
    const #27 = NameAndType #34:#35;//  out:Ljava/io/PrintStream;
    const #28 = class   #36;    //  java/io/PrintStream
    const #29 = NameAndType #37:#18;//  println:(Ljava/lang/String;)V
    const #30 = NameAndType #17:#18;//  foo:(Ljava/lang/String;)V
    const #31 = Asciz   Test;
    const #32 = Asciz   java/lang/Object;
    const #33 = Asciz   java/lang/System;
    const #34 = Asciz   out;
    const #35 = Asciz   Ljava/io/PrintStream;;
    const #36 = Asciz   java/io/PrintStream;
    const #37 = Asciz   println;
    
    {
    java.lang.String s1;
    
    java.lang.String s2;
    
    public Test();
      Code:
       Stack=2, Locals=1, Args_size=1
       0:   aload_0
       1:   invokespecial   #1; //Method java/lang/Object."[init]":()V
       4:   aload_0
       5:   ldc #2; //String Hello world!
       7:   putfield    #3; //Field s1:Ljava/lang/String;
       10:  aload_0
       11:  ldc #2; //String Hello world!
       13:  putfield    #4; //Field s2:Ljava/lang/String;
       16:  return
      LineNumberTable:
       line 1: 0
       line 2: 4
       line 3: 10
    
    
    public void foo(java.lang.String);
      Code:
       Stack=2, Locals=2, Args_size=2
       0:   getstatic   #5; //Field java/lang/System.out:Ljava/io/PrintStream;
       3:   ldc #2; //String Hello world!
       5:   invokevirtual   #6; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8:   getstatic   #5; //Field java/lang/System.out:Ljava/io/PrintStream;
       11:  aload_1
       12:  invokevirtual   #6; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
       15:  return
      LineNumberTable:
       line 5: 0
       line 6: 8
       line 7: 15
    
    
    public void bar();
      Code:
       Stack=2, Locals=1, Args_size=1
       0:   aload_0
       1:   ldc #2; //String Hello world!
       3:   invokevirtual   #7; //Method foo:(Ljava/lang/String;)V
       6:   return
      LineNumberTable:
       line 9: 0
       line 10: 6
    
    
    }
  • 17. 1. 2013 13:27

    kvr kvr

    To jsem nemyslel, tam to zprocesuje kompilátor a nakonec při loadování intern().

    Ale když mám "Hello", "World", "Bye", tak že by při loadování udělal char[15] = "Hello\0World\0By­e\0" a z toho vytvořil tři instance String s offset 5, 7 a 12. Tím by se eliminoval overhead nad tím polem.

  • 17. 1. 2013 17:16

    Pavel Tišnovský
    Zlatý podporovatel

    Aha chapu. Ve zdrojacich jsem nasel pouze kontrolu, zda se pri nacitani stringu z constant poolu nejedna o duplicitu - potom se novy symbol nevytvari. K tomu jednomu poli pro vsechny string - podle vseho se kazdy retezec vytvari s nulovym offsetem (funkce Handle java_lang_Strin­g::basic_crea­te(int length, bool tenured, TRAPS)

  • 17. 1. 2013 18:18

    Natix (neregistrovaný)

    Sdílení char polí se děje určitě v metodě String.substring(), což může působit pěkný memory leak, pokud se udělá malý substring z nějakého obludně dlouhého.

    public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > count) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        if (beginIndex > endIndex) {
            throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
        }
        return ((beginIndex == 0) && (endIndex == count)) ? this :
            new String(offset + beginIndex, endIndex - beginIndex, value);
    }
    
    // Package private constructor which shares value array for speed.
    String(int offset, int count, char value[]) {
        this.value = value;
        this.offset = offset;
        this.count = count;
    }
  • 17. 1. 2013 18:22

    Pavel Tišnovský
    Zlatý podporovatel

    A pouziva se to v mnoha programech - nacteni XML, vykousnuti jednoho elementu a "zapomenuti" puvodniho retezce. To cele nekde ve smycce :)

  • 17. 1. 2013 12:29

    Rax (neregistrovaný)

    >>> sys.getsizeof("a")
    25
    >>> sys.getsizeof("ě")
    26

    V Pythonu je údajně více možností pro interní uložení stringu, ale tato prokazatelně používá UTF-8 a to je overhead jako parní lokomotiva. To nikdo hned tak nepřekoná.

  • 17. 1. 2013 16:33

    Pavel Tišnovský
    Zlatý podporovatel

    jj, proto jsem pouzil ASCII znaky a nejaka ta nabodenicka. V soucasnosti fakt nevim, co je nejlepsi pouzivat INTERNE, vetsinou by melo stacit UCS-2 kvuli relativne male narocnosti na pamet nebo UCS-4 by mohlo byt rychlejsi, zase 4 byte/znak je taky overhead.

  • 17. 1. 2013 17:03

    Rax (neregistrovaný)

    Drtivá většina civilizovaného světa interně jede takhle, protože se to ukázalo jako nejpoužitelnější:
    - 8-bitů na znak v defaultní kódové stránce OS.
    - 16-bitů na znak v UCS-2
    - občas se vyskytne 8-bitů na znak + 16-bitů informace o kódové stránce 1x pro celý string.

    Jistý majoritní OS jede interně celý v UCS-2, ale některé funkce podporují UTF-16.