Строковые литералы, использующие в 2 раза больше ожидаемого объема пространства постоянного поколения - PullRequest
5 голосов
/ 21 февраля 2011

Это Sun JDK 1.6u21, x64.

У меня есть класс для экспериментов с использованием разрешений, который содержит только одну большую строку (512 тыс. Символов):

public class Big0 {
     public String bigString =
         "A string with 2^19 characters, should be 1 MB in size";
}

Я проверяю использование perm gen с помощью getUsage().toString() для объекта MemoryPoolMXBean для постоянного поколения (называемого «PS Perm Gen» в u21, хотя у него немного разные имена с разными версиями или с разными сборщиками мусора.

Когда я впервые обращаюсь к классу, скажем, читая Big0.class, perm gen скачет на ~ 500 КБ - это то, что я ожидаю, так как кодировка строки в постоянном пуле - UTF-8, и я используютолько символы ASCII.

Однако, когда я на самом деле создаю экземпляр этого класса, perm gen увеличивается на ~ 2 МБ. Поскольку это 1 МБ строки в памяти (2 байта на символ UTF16, конечно, никаких суррогатов)), Я запутался, почему использование памяти удваивается.

Тот же эффект возникает, если я делаю строку статической. Если я использовал final, он не компилируется, так как я превышаю предел для coэлементы постоянного пула размером 65535 байт (не уверен, почему этого не следует делать и при окончательном отключении - считайте, что это бонусный вопрос).

Любое понимание приветствуется!

Редактировать: Я долженТакже отметим, что это происходит с нестатическими, конечными нестатическими и статическими строками, но не с окончательными статическими строками.Поскольку для строковых констант это уже лучшая практика, возможно, это в основном представляет академический интерес.

Ответы [ 4 ]

2 голосов
/ 23 февраля 2011

Я думаю, что это артефакт вашего тестового класса.Я создал подобный класс, а затем декомпилировал его с помощью javap.

Компилятор java [eclipse] разбивает литерал String на куски, каждый длиной не более 64 КБ.Байт-код для инициализации непостоянного поля состоит из объединения исходной строки с последовательностью операций StringBuilder.Хотя эта заключительная гигантская строка является интернированной, большие атомы, из которых она состоит, занимают пространство в постоянном пуле.

0 голосов
/ 21 февраля 2011

Хотя формат файла класса определяет модифицированный UTF-8 в качестве формата хранения для литералов String, внутренний формат среды выполнения - UTF-16.A String хранит свои данные в кодировке UTF-16 в char[] (обычно это зависит от реализации, однако).Большинство символов занимают 2 байта в этой кодировке (символы вне BMP занимают больше).

Я видел ссылки на модифицированный rt.jar, который содержит реализацию java.lang.String со специализированным кодом-путем /хранилище для ASCII-строк, что значительно сокращает требования к памяти.

Редактировать: кажется, что эта опция вошла в обычную Oracle JRE после Java 6 Update 21 согласно этой ссылке :

-XX: -XX: + UseCompressedStrings

Использовать байт [] для строк, которые могут быть представлены как чистый ASCII.(Представлено в Java 6 Update 21 Performance Release)

(Найден через этот ответ ).

0 голосов
/ 21 февраля 2011

Хороший профилировщик памяти (я лично использую и очень люблю yourkit java profiler) должен быть в состоянии показать вам, где используется память.

0 голосов
/ 21 февраля 2011

Символы Java имеют ширину 2 байта на символ (независимо от того, является ли это ASCII или кодовой точкой выше 255).Я думаю, что то, что вы видите, - это виртуальная машина Java, переводящая версию строки внутреннего хранилища файлов (измененный UTF8) во внутреннюю расширенную форму, как только класс инициализирован (что делается до создания экземпляра)

...