Почему объем памяти кучи растет вместе с метапространством в Java 8?
Я делаю небольшой тест, чтобы понять, как работает метапространственная память (Java 8 и выше). Когда я создаю 100 000 классов динамически, память метаскопа растет (очевидно), но память кучи также растет. Может кто-нибудь объяснить мне, почему это происходит?
PS: я запускаю тест с 128 МБ кучи и 128 МБ метапространства.
@Test
public void metaspaceTest() throws CannotCompileException, InterruptedException {
ClassPool cp = ClassPool.getDefault();
System.out.println("started");
for (int i = 0; i <= 100000; i++) {
Class c = cp.makeClass("br.com.test.GeneratedClass" + i).toClass();
Thread.sleep(1);
if (i % 10000 == 0) {
System.out.println(i);
}
}
System.out.println("finished");
}
Смотрите изображения ниже:
2 ответа
Я сделал исследование вашего кода особенно ClassPool#makeClass
, Я заметил несколько моментов, которые приводят к увеличению пространства кучи по мере увеличения метапространства.
- Это
cache
классы, созданные методом makeClass внутри хеш-таблицы
защищенные классы Hashtable;
Таким образом, для десятого миллиона классов есть запись для каждого из них. Следовательно, пространство кучи также увеличивается, и это не GC, поскольку ссылка на хеш-таблицу все еще используется вашим циклом for и постоянно обновляется, поэтому не имеет права на gc.
CtNewClass
создает новый экземпляр класса и имеет определение конструктора, как показано ниже:CtNewClass(String name, ClassPool cp, boolean isInterface, CtClass superclass) { super(name, cp); this.wasChanged = true; String superName; if (!isInterface && superclass != null) { superName = superclass.getName(); } else { superName = null; } this.classfile = new ClassFile(isInterface, name, superName); if (isInterface && superclass != null) { this.classfile.setInterfaces(new String[]{superclass.getName()}); } this.setModifiers(Modifier.setPublic(this.getModifiers())); this.hasConstructor = isInterface; }
В коде выше строки this.classfile = new ClassFile(isInterface, name, superName);
фактически создает новый экземпляр ConstPool для каждого класса, т.е. новые экземпляры HashMap для каждого экземпляра и эти резервные памяти в пространстве кучи.
Классы HashMap; // из класса ConstPool
HashMap строки; // из класса ConstPool
Кроме того, это создает два новых ArrayLists. соблюдать this.fields = new ArrayList();
а также this.methods = new ArrayList();
утверждение в конструкторе выше. Также появился новый связанный список this.attributes = new LinkedList();
,
Следовательно, вывод заключается в том, что ClassPool имеет управление кешем, которое занимает много места в куче. Затем у каждого класса есть свой набор коллекций для управления свойствами, константами и т. Д.
Надеюсь, поможет!
Ваш пул классов использует кучу памяти. У него есть хэш-таблицы, списки и другие вещи. Он также использует код отражения Java, который использует кучи памяти. Куча памяти, которая не является gc'ed, это, вероятно, все эти структуры данных в пуле классов, пара хеш-таблиц, связанные списки, списки массивов, пулы и т. Д. Например, каждый создаваемый вами класс хранится в хеш-таблице класса. бассейн. Это хэш-таблица на 100000 элементов.
Во-вторых, если в создаваемых вами классах есть статические инициализаторы, они будут использовать кучу памяти. Статические инициализаторы включают в себя инициализации статического поля и код статического блока.