Подтвердить что ты не робот

Как восстановить память после разбора с помощью подстрок? intern() или новый String()?

Краткая версия: если вы вызываете string.substring(n, m).intern(), сохраняет ли таблица строк подстроку или исходную строку?

... Но я не уверен, что правильный вопрос спросить, так вот длинная версия:

Я работаю с устаревшим Java-кодом (PCGen), который анализирует файлы, разбивая их как одну большую строку, а затем используя String.split,.trim,.substring и StringTokenizer, чтобы разложить их на токены. Это очень эффективно для синтаксического анализа, потому что ни один из этих методов не копирует исходную строку, но все указывает на части общего char [].

После завершения синтаксиса я хочу вернуть некоторую память. Нужно лишь несколько небольших подстрок оригинальной большой строки, но сильная ссылка не позволяет собирать большую строку. И позже я страдаю OOM, я полагаю, отчасти из-за этого огромного влияния кучи множества проанализированных файлов.

Я знаю, что я могу обрезать большую строку вниз с помощью new String(String) (copy-on-write). И я знаю, что могу уменьшить дублирование строк через String.intern(что важно, потому что в анализируемых файлах много избыточности). Нужно ли использовать оба, чтобы восстановить наибольшее количество кучи, или делает .intern() и то и другое? Читая исходный код OpenJDK7 hotspot (hotspot/src/share/vm/classfile/symbolTable.cpp), похоже, что таблица строк хранит целую строку и вообще не обрезает ее для смещения/длины. Поэтому я думаю, что мне нужно создать новую String, а затем стажер. Правильно?

Все сказанное, переключение на потоковый синтаксический анализатор будет большой победой в плане памяти, но это слишком большое изменение на короткий срок.

4b9b3361

Ответ 1

Вы можете использовать новый метод String (String) и intern(), и для этого потребуется копия по мере необходимости для обновления до версии Java 7. 4. Из обновления Java 7 5 подстрока будет занимать более глубокую копию, но вы все равно можете захотеть для использования intern(). Примечание. Java 7 использует кучу, а не perm gen для хранения литералов String.

public static void main(String[] args) {
    char[] chars = new char[128];
    Arrays.fill(chars, 'A');
    String a128 = new String(chars);
    printValueFor("a128", a128);
    String a16 = a128.substring(0, 16);
    printValueFor("a16", a16);
}

public static void printValueFor(String desc, String s) {
    try {
        Field value = String.class.getDeclaredField("value");
        value.setAccessible(true);
        char[] valueArr = (char[]) value.get(s);
        System.out.println(desc + ": " + Integer.toHexString(System.identityHashCode(valueArr)) + ", len=" + valueArr.length);
    } catch (Exception e) {
        throw new AssertionError(e);
    }
}

на Java 7 обновление 4 печати

a128: 513e86ec, len=128
a16: 53281264, len=16

Я бы ожидал, что Java 6 этого не сделает.

Ответ 2

Мы можем проверить это. Строка содержит свой массив символов в поле

   private final char value[];

посмотрим, что произойдет после подстроки(); стажер();

    Field f = String.class.getDeclaredField("value");
    f.setAccessible(true);
    String s1 = "12345";
    String s2 = s1.substring(1, 2);
    String s3 = s2.intern();
    System.out.println(f.get(s2) == f.get(s1));
    System.out.println(f.get(s3) == f.get(s2));

Выход

true
true

то есть все 3 строки имеют один и тот же массив символов