Использование Java и метаданных Java8

У меня есть этот код для динамического создания класса и загрузки его

import javassist.CannotCompileException;
import javassist.ClassPool;

public class PermGenLeak {
    private static final String PACKAGE_NAME = "com.jigarjoshi.permgenleak.";

    public static void main(String[] args) throws CannotCompileException, InterruptedException {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            ClassPool pool = ClassPool.getDefault();
            pool.makeClass(PACKAGE_NAME + i).toClass();
            Thread.sleep(3);
        }

    }
}

Я запустил этот класс против Java 7 (jdk1.7.0_60) и, как и ожидалось, он заполнил PermGenSpace, а куча осталась неиспользованной Java 7 memory usage изображение показывает сверхурочное использование сверхурочного времени и в конце JVM завершено

Теперь тот же код работал против Java 8 (jdk1.8.0_40-ea), и, как и ожидалось, он продолжал расширять собственную память (Metaspace), но удивительно, что для 1g Metaspace он потреблял 3g кучи в OldGen (почти 3x из Metaspace поддерживались времени)

Java8 memory usage изображение показывает сверхурочное время использования метапроцесса и пример использования системной памяти

это письмо от Jon Masamitsu и этот билет JEP говорит

интернированный String и статистика классов, а некоторые разные данные были перенесены в кучу

что именно делает это увеличение кучи при загрузке большего количества классов в Metaspace?

Ответы

Ответ 1

Запустите jmap -histo PID, чтобы увидеть, какие объекты потребляют пустое пространство.
Когда я запустил ваш пример, я увидел кучу, полную вспомогательных объектов Javassist:

 num     #instances         #bytes  class name
----------------------------------------------
   1:        592309      312739152  [Ljavassist.bytecode.ConstInfo;
   2:       6515673      208501536  java.util.HashMap$Node
   3:       2964403      169188824  [C
   4:       1777622      102165184  [Ljava.lang.Object;
   5:       4146200       99508800  javassist.bytecode.Utf8Info
   6:       3553889       85293336  java.util.ArrayList
   7:       2964371       71144904  java.lang.String
   8:        593075       56944008  java.lang.Class
   9:        592332       47388032  [Ljava.util.HashMap$Node;
  10:        592309       37907776  javassist.bytecode.ClassFile
  11:        592308       37907712  javassist.CtNewClass
  12:       1185118       28555808  [B
  13:        592342       28432416  java.util.HashMap
  14:       1184624       28430976  javassist.bytecode.ClassInfo
  15:        592309       28430832  [[Ljavassist.bytecode.ConstInfo;
  16:        592322       23692880  javassist.bytecode.MethodInfo
  17:        592315       23692600  javassist.bytecode.CodeAttribute
  18:        592434       18957888  java.util.Hashtable$Entry
  19:        592309       18953888  javassist.bytecode.ConstPool
  20:        592308       18953856  java.lang.ref.WeakReference
  21:        592318       14215632  javassist.bytecode.MethodrefInfo
  22:        592318       14215632  javassist.bytecode.NameAndTypeInfo
  23:        592315       14215560  javassist.bytecode.ExceptionTable
  24:        592309       14215416  javassist.bytecode.LongVector
  25:        592309       14215416  javassist.bytecode.SourceFileAttribute
  26:        592507        9487584  [I
  27:             8        6292528  [Ljava.util.Hashtable$Entry;
  28:           212          18656  java.lang.reflect.Method
  29:           407          13024  java.util.concurrent.ConcurrentHashMap$Node
  30:           124           8928  java.lang.reflect.Field

Ответ 2

что именно делает это увеличение кучи при загрузке большего количества классов в Metaspace?

Моя гипотеза заключается в том, что это "обычный" мусор, который создается вашим примером. Я предполагаю, что:

  • Код javaassist создает обычные объекты кучи. Они в основном "большие", и это заставляет их выделяться непосредственно в кучу OldGen. Или что-то еще вызывает это.

    ( ОБНОВЛЕНИЕ - глядя на @apangin Отвечать, теперь я подозреваю, что они начались в куче YoungGen и были наняты...)

  • Когда classLoader.defineClass вызывается под капотом, он создает объекты в метапространстве из массива байтов, содержащего файл класса.

  • Использование OldGen остается... потому что ничто не вызвало полное GC еще.

Если вы изменили свой пример, чтобы классы были доступны, а затем принудительно заполнили полный GC, я бы ожидал (надеюсь) увидеть, что использование OldHeap падает, что указывает на то, что это "обычный" мусор, а не утечка хранилища.