При поиске проблем с производительностью JVM вы можете столкнуться с утечками памяти и привести к тому, что JVM Outpmemory. Если параметр reloadable = "true" устанавливается при использовании контейнера Tomcat, вы также можете столкнуться с переполнением памяти при частое развертывание приложений. Принцип горячего развертывания Tomcat состоит в том, чтобы обнаружить, что файлы в Web-Inf/Classes или Web-Inf/Lib Directory изменяются, а приложение будет остановлено сначала, а затем запустилось. Поскольку Tomcat назначает загрузку WebAppClass -Class по умолчанию, принцип горячей замены состоит в том, чтобы создать новый ClassLoader для загрузки класса. Поскольку уникальность класса в JVM определяется файлом класса и загрузчиком класса, перезагрузка класса может достичь цели горячей замены. Когда количество горячих развертываний будет более частым, оно приведет к тому, что больше классов загружается JVM. Если предыдущий класс не выгружен вовремя по какой -то причине (например, утечки памяти), это может привести к постоянному генерации или Metaspace. В этой статье кратко представлены сценарий, в котором Threadlocal и ClassLoader приводят к утечкам памяти и, в конечном итоге, превышают демонстрацию.
Удаление класса
После использования класса, если следующая ситуация будет удовлетворена, она будет удалена:
1. Все экземпляры этого класса в куче были переработаны, то есть объекты экземпляра этого класса не существуют в куче.
2. Загрузка класса, загружающий этот класс, был переработан.
3. Объект класса, соответствующий этому классу, не может быть упомянут нигде, и объект класса не может быть доступен через отражение.
Если класс соответствует условиям удаления, JVM удаляет класс, когда он находится в GC, то есть очищает информацию класса в области метода.
Введение сцены
В предыдущей статье я представил принцип Threadlocal. Каждый поток имеет нитокалмап. Если жизненный цикл потока является относительно длинным, запись в ThreadlocalMap не может быть переработана. На нитолокальный объект всегда был пристально ссылаться на поток. Поскольку объект экземпляра будет содержать ссылку на объект класса, объект класса будет хранить ссылку на загрузку класса, который его загружает, что приведет к выгрузку класса. Когда загружается достаточно классов, может произойти переполнение постоянной генерации или метаспаса. Если у класса есть большие объекты, такие как больший массив байтов, это приведет к переполнению памяти областей кучи Java.
Введение исходного кода
Вот внутренний класс внутренний. Внутренний класс имеет статический ниточный объект, который в основном используется для того, чтобы потоки содержали прочные ссылки на внутренний класс, так что внутренний класс не может быть переработан. Пользовательский загрузчик класса определяется для загрузки внутреннего класса, как показано ниже:
Открытый класс MemoryLeak {public static void main (string [] args) {// Поскольку поток работает все время, внутренний объект в ThreadlocalMap сильно ссылался на объект потока Новый поток (новый Runnable () {@Override public void run () {where (true) {// Каждый раз, как New ClassLoadErult Sacetroader, нагрузка Customlasclass Classclass Classclass Classclass Classclasclasclasclasclasclasclasclasclasclasclasclasclasclasclasclasclasclascloarder. (Load1 », Memoryleak.class.getClassloader (),« com.ezlippi.memoryLeak $ inner »,« com.ezlippi.memoryLeak $ inner $ 1 »); GC для обработки innerclass = null; } // Чтобы добраться до области кучи быстрее общедоступного статического класса Inner {Private Byte [] MB = новый байт [1024 * 1024]; static threadlocal <inner> threadlocal = new Threadlocal <Nner> () {@Override Protected innerValue () {return new inner (); }}; // вызовать threadlocal.get () для инициализации внутреннего объекта static {threadlocal.get (); } public inner () {}} // исходный код опустить частный статический класс CustomClassLoader Extends ClassLoader {}Переполнение памяти с кучей
Чтобы запустить переполнение памяти кучи, я установил массив байтов 1 МБ во внутреннем классе, и в то же время я должен вызвать Threadlocal.get () в статическом блоке. Только вызов запустит initialValue () для инициализации внутреннего объекта, в противном случае я просто создам пустой темлокальный объект, и в потоке нет данных.
Параметры JVM следующие:
-Xms100m -xmx100m -xx:+useparnewgc -xx:+useconcmarksweepgc -xx:+printgcdetails -xx:+printheapatgc -xx:+printclasshistogram -xx:+heapdumponoutofmoryerror
После последних 814 выполнений переполняется память о куче JVM, как показано ниже:
java.lang.outofmemoryerror: java -huep -разрозненная куча до java_pid11824.hprof ... создан файл свалки кучи [100661202 байт в 1,501 секунды]. 99% использовали [0x00000000f9c000000, 0x0000000000fb6ad450, 0x0000000000fb6b0000) от пространства 3392K, 90% использовались [0x000000000000fb6b0000, 0x00000000fb9b0030, 0x00000000000). [0x000000000000fba00000, 0x000000000000FBD500000) Совместная генерация Mark-Sweep Всего 68288K, использован 67600K [0x000000000FBD500000, 0x0000001000000000, 0x0000000100000). Пространство использовалось 474K, емкость 578K, совершенная 640K, зарезервировано 1048576kexception в потоке "Thread-0" java.lang.outofmemoryerror: java gup space at com.ezlippi.memoryleak $ inner. sun.reflect.nativeconstructoracccessoriMpl.newinstance0 (нативный метод) в sun.reflect.nativeconstructoracccessorimpl.newinstance (неизвестный источник) на sun.reflect.delegatingConstructorAccessOrmpl.newinStance (неизвестный источник). java.lang.reflect.constructor.newinstance (неизвестный источник) источник) на java.lang.class.newinstance (неизвестный источник) на com.ezlippi.memoryLeak $ 1.Run (MemoryLeak.java:20) на java.lang.Thread.Run (неизвестный источник)
Вы можете видеть, что у JVM нет памяти для создания нового внутреннего объекта, потому что область кучи хранит много массивов байтов 1 МБ. Здесь я распечатал гистограмму класса (следующая рисунок представляет собой сцена с размером кучи 1024 м), пропуская некоторые незначительные классы. Видно, что байтовая массива занимает 855 м пространства, и создаются 814 экземпляров com.ezlippi.memoryLeak $ CustomClassLoader, что в основном соответствует размеру байтового массива:
num #instances #bytes class имя------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------. com.ezlippi.memoryLeak $ CustomClassLoader 12: 820 53088 [ljava.Util.hashtable $ inting; 15: 817 39216 java.util.hashtable 16: 915 36600 java.lang.ref.softreference 17: 543 34752 java.net.url 18: 697 33456 java.nio.heapcharbuffer 19: 817 32680 Javaceration.Sprone.prostection. java.util.treemap $ intry 21: 928 29696 java.util.hashtable $ 22: 1802 28832 java.util.hashset 23: 817 26144 Java.security.codesource 24: 814 26048 java.lang.threadlocal $ rathocalmap $ rathocalmap $ rathocalmap $ rathocalmap $ rathocalmap $ rathocalmap $ rathocalmap $ rathrocal
Metaspace переполнен
Чтобы сделать переполнение Metaspace, вы должны немного уменьшить пространство Metaspace и загрузить достаточно классов перед переполнением кучи. Поэтому я настраивал параметры JVM и отрегулировал размер массива байтов до 1 КБ, как показано ниже:
Частный байт [] kb = новый байт [1024]; -xms100m -xmx100m -xx:+useparnewgc -xx:+useconcmarksweepgc -xx:+printgcdetails -xx:+printheapatgc -xx:+printclegram -xx: metaspacesisize = 2mmamemem.mesm.m.saspacesisize: 2mmamemem.m.saspacesize: 2mmamemem.
Из журнала GC мы видим, что когда Meraspace достигнет порога GC (то есть, размер конфигурации MaxMetaspAcesize) FullGC будет запускается:
java.lang.outofmemoryerror: metaspace << отсутствие трассировки стека >> {Heap до GC Enlocations = 20 (Full 20): Par New Generation Total 30720K, используется 0K [0x000000000F9C000000, 0x00000000FBD500000) EDEN Space 27328K, 0% [0x00000000FD500000) Eden Space 27328K, 0% [0x00000000fbd500000) Eden 27328K, 0% [0x00000000fbd500000). 0x00000000F9C00000, 0x0000000F9C00000, 0x0000000F9C00000, 0x0000000FB6B0000) от пространства 3392K, 0% использовалось [0x000000000000FB6B0000, 0x00000000FB6B0000, 0x0000000000000). [0x000000000000fba00000, 0x000000000FBD500000) в пространство 3392K, 0% использовалось [0x000000000000FBA00000, 0x000000010000000000) Metaspace использовал 1806K, емкость 1988K, Commoned 2048K, Reserved 1056768K Class Space Space Cresess Class. 1048576K [Полный GC (порог GC Metadata) [CMSProcess завершен с кодом выхода 1Из приведенного выше примера мы видим, что если загружатель класса и нитолокаль используются неправильно, это действительно приведет к утечке памяти. Полный исходный код находится в GitHub