вівторок, 22 лютого 2011 р.

Кеши значений в Java

Java полна нюансов. Казалось бы Integer себе, чего в нем особенного. Нет, копнуть глубже можно везде, в любом месте. Вот и в Integer, оказывается, кеширование присутствует.

В общем, уже пару недель как я учу кунг-фу, поэтому решил собрать в одном месте информацию по внутренним кешам Java.

Собственно их мне известно три.

Кеш строк

Многие знают, а кто не знает - узнают, что в Java строковые константы часто оказываются равны. Это называется интернированием. Например

public class Nan {
    public static void main(String[] args) {
        CharSequence cs = "hello";
        String s = "hello";
        System.out.println(cs == s);
    }
}

--
true

С помощью интернирования JVM экономит память выделенную под строки, поддерживая пул строковых констант. Поскольку строки - неизменяемы, то у строк с одинаковым изначальным значением, всегда будет одно и то же состояние, то есть они взаимозаменяемы, чем JVM и пользуется.

С другой стороны, если вам все же нужно различать одинаковые строки, пользуйтесь оператором new.

public class Nan {
    public static void main(String[] args) {
        String s1 = new String("hello");
        String s2 = new String("hello");
        System.out.println(s1 == s2);
    }
}

-- 
false

Интернирование может быть сделано вручную с помощью метода intern().

public class Nan {
    static String concat(String x, String y) {
        return x + y;
    }
    static void showTruth(String x, String y) {
        System.out.println("ref: " + (x == y) + "\tintern: " + (x == y.intern()));
    }

    public static void main(String[] args) {
        String s = "Hello";
        showTruth(s, "Hello");
        showTruth(s, new String("Hello"));
        showTruth(s, concat("He", "llo"));
    }
}
--
ref: true intern: true
ref: false intern: true
ref: false intern: true


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

Кеши целочисленных оберток

В каждом из классов Long, Integer, Short и Byte присутствует внутренний кеш ссылок на значения от -128 до 127, причем в каждом из классов он реализован по-разному. Лучи ненависти в сторону Sun направляют противники копипасты под негодующие крики ненавистников Java. Что интересно, в Sun JVM присутствует возможность во время выполнения изменить верхний (и только!) предел кеша для Integer (и только!) на произвольное значение с помощью опции -XX:AutoBoxCacheMax или выставив системный параметр java.lang.Integer.IntegerCache.high в необходимое значение

В общем и кратко:

public class Nan {
    public static void main(String[] args) {
        Integer i1 = 10;
        Integer i2 = 10;
        System.out.println(i1 == i2);

        i1 = 128;
        i2 = 128;
        System.out.println(i1 == i2);
    }
}

--
true
false

-- с -Djava.lang.Integer.IntegerCache.high=128
true
true

Внутренний кеш используется при использовании метод valueOf у оберток, что видно в исходном коде, а автобоксинг проиcходит через вызов valueOf. Все значения кеша инициализируются в статическом блоке, что незначительно бьет по скорости первого использования Integer (только не это !).

Кеш логических значений

У обертки Boolean также присутствует кеширование. Его valueOf всегда возвращает ссылку на один из двух внутренних экземпляров: Boolean.TRUE или Boolean.FALSE. В отличии от String у Boolean нету метода intern() поэтому бездумное использование конструктора приведет к перерасходу памяти. Лучше всего всегда-всегда использовать valueOf.

Заключение (censored)

Кеши - вещь, полезная в хозяйстве для любителей поспорить на несколько сотен и блеснуть своей <*> перед коллективом. Как по-мне, целочисленные кеши бесполезны в большинстве задач, где обычно просто нужно забоксить пару значений, а вероятность попадания в кеш ничтожная. Если и есть какой-то выигрыш при итерациях, то для таких случаев можно было б придумать чтото поэлегантней, а не прикручивать <!> к <*>, который к тому же и память занимает в несколько килобайт. К интернированию строк и кешу булевых значений претензий не имею, вполне логично и правильно.

2 коментарі:

  1. Здравствуйте!

    Скажите пожалуйста, как мне насторить выиграш при интеграциях в Мега Миллион Результаты http://localotto.com/ru/megamillions лотереи? Очень бы хотелось от Вас услышать, как мне можно это сделать.

    ВідповістиВидалити
  2. Здравствуйте!

    Скажите пожалуйста, как я могу проверить powerball результаты http://localotto.com/ru/powerballusa на своем планшете за 2017 год? Никак сайт не открывается(( Подскажите пожалуйста что делать!

    ВідповістиВидалити