Данная публикация посвящается всем ценителям Minecraft и других игр, требующих установки Java. На днях знакомые попросили помочь: при попытке запустить Майнкрафт у них появлялось сообщение — Java Virtual Machine Launcher ошибка, как исправить сейчас расскажу.
При установке Джавы, необходимой для работы некоторых игр, отображалось следующее окно:
Она указывает на то, что для создаваемой виртуальной машины не хватает памяти. Очень часто подобная ситуация возникает, если некорректно выходить из игры (нажав Alt + F4 ) или при внезапном отключении ПК.
В моем случае, ошибка появилась после того, как пользователь случайно удалил компонент Java, потом пытался его переустановить, но на последнем этапе инсталляции отображалось вышеупомянутое окно.
На зарубежных источниках нашел два решения. Начну с более простого.
Теперь при запуске установщика Джава ошибка Java Virtual Machine Launcher не появиться.
Переходим ко второму способу.
Покажу всё на примере Minecraft.
Вы узнали, как исправить ошибку Java Virtual Machine Launcher. Но если вопросы остались, обязательно задавайте их с помощью формы комментирования внизу страницы.
Источник
Invalid or corrupt jarfile
В IntelliJ IDEA-14 создал проект, в параметрах версия Java 1.8, на моем компе запускается и из IDEA, и jar-артифакт. Перенес jar на другой комп, поставил там Java 1.8.66 распоследнюю — пишет такую ошибку при запуске. Про manifesrt ничего не знаю пока, буду читать еще.
Как вообще надо создавать нормальные стандалон-приложения на java?
Скорее всего у тебя действительно повредился файл в процессе переноса.
Ладно, попробую перенести еще раз (как доберусь до компа с исходниками), хотя сомнительно что проблема в этом.
Jar-файл это просто ZIP-архив. Попробуй его разархивировать любым архиватором. Если получится — можно дальше думать. Если не получится — значит файл повреждён.
Фигасе, действительно обычный архив! 🙂 Открывается, там оказывается запихнуто все что надо и не надо из сорцов проекта. Файл вроде не поврежден, как я и подозревал.
Покажи вывод jar tf usergrid-launcher-1.0-SNAPSHOT.jar
Виндовая (знаю где я, не бейте :)) недоконсоль закрывается и не дает прочитать что там написано. Щас буду пробовать победить это и покажу.
«jar» не является внутренней или внешней командой, исполняемой программой или пакетным файлом.
jdk установи для начала
Хороший вопрос. Могу установить конечно, но зачем мне он на машине, где я хочу только запускать готовые приложения? Или Java-программы не будут работать на компах где есть только jre?
Будут, естественно. Но если вам нужно разрабатывать, вам нужен jdk. Почему вы удивляетесь тому, что у вас нет команды jar, если не установлен jdk?
Понял, ради этой команды сейчас качаю и установлю jdk последний. Хотя на этом компе я хотел только запустить программку, которую написал на другом, где она запускается (и где конечно и jre и jdk).
Поставил jdk, написала саксессфулли инсталлед, но jar и javac команды консоль до сих пор не знает. Наверное надо патхи приписывать руками. Не знаю в чем проблема. Как в 17 веке прямо все — консоль, ручное подключение.
В каталоге установленных программ теперь 2 папки — jre1.8.0_66 и jdk1.8.0_66, в которой своя подпапка jre. java -version в консоли пишет версию jre, javac и jar не работают. Перегружался. Монитор протирал. Мыслей нет.
Источник
Fix Minecraft Forge Error Invalid or Corrupt Jarfile
Up next
The Sims 4 is Already Running | Fix This Error on PC and Mac
Author
Steven P. Ross
Share article
Error: Invalid or Corrupt Jarfile. This is a common Minecraft error, and the fix for this issue is quite simple.
The “how to check if jar file is corrupted” is a common error that can occur when users try to install Minecraft Forge. The good news is that there are many ways for users to fix the issue.
If you’re having trouble installing Minecraft Forge due to an invalid or corrupt jarfile issue, keep reading to find out what’s causing it and how to repair it.
How to Repair a Corrupt or Invalid Jarfile in Minecraft Forge
1. Double-check that the download is complete.
You’ll receive a variety of problems if you attempt to run the game before it’s finished downloading, including the invalid or corrupt jarfile error.
Check to see whether Minecraft Forge has shown in your downloads folder. Then, make sure it’s not being blocked by your browser for any reason.
Recover the file in your browser’s download manager and try again.
2. Switch to a different browser.
Many of the customers who reported invalid or corrupt jarfile problems in Minecraft downloaded the game using Firefox.
The Minecraft Forge download seems to be often blocked by Firefox. As a result, we recommend that you try downloading the game using a different browser.
3. Install the most recent version of Java.
Make sure your computer has the most recent Java version installed. If you’re using an older version, download a newer one.
4. Turn off all background applications.
Remember to close all background applications and processes before installing Minecraft Forge or any other game or software on your computer.
Some of the applications that are currently operating on your computer may prevent the download from taking place. Alternatively, they may cause Minecraft Forge to crash when you attempt to load the launcher.
Using Task Manager or a clean start of your computer, you may close background applications.
If you’d rather utilize Task Manager, go to the Processes tab and right-click on the applications you wish to turn off. Then choose Finish Task.
You may also turn off your antivirus and firewall for a while. Certain Minecraft files may be erroneously flagged as suspicious by these applications, causing them to be blocked.
After you’ve downloaded and installed Minecraft Forge, remember to re-enable your antivirus and firewall.
We’re hoping for some assistance.
Frequently Asked Questions
How do I fix invalid or corrupt Jarfile?
A: Delete the file and then re-download it from our website.
How do I fix Minecraft Forge error?
A: The error you are receiving is most likely related to the Minecraft Forge. To fix this, try updating your version of minecraft. If that doesnt work, try using a different launcher such as MCPatcher or Mod Organizer
- invalid or corrupt jarfile windows 10
- how to fix unable to access jarfile
- error: invalid or corrupt jarfile mac
- shockbyte invalid or corrupt jarfile
- unable to access jarfile forge
Hi, I’m Steven. I run this Blog myself and I’ve been in the electronics industry for a while so I know my stuff. The guides on this website are well researched and I also have experience with most of them. Do comment if you think some information is not correct on a particular page.
Источник
*** MOROZILKA ***
Меню навигации
Пользовательские ссылки
Информация о пользователе
Вы здесь » *** MOROZILKA *** » другие сервера » не могу запустить Minecraft — Invalid or corrupt jarfile
не могу запустить Minecraft — Invalid or corrupt jarfile
Сообщений 1 страница 12 из 12
Поделиться103-06-2012 03:37:59
- Автор: FantOzer
- Админ
- Откуда: Новосибирск
- Зарегистрирован : 09-12-2009
- Приглашений: 0
- Сообщений: 10032
- Уважение: +496
- Позитив: +598
- Пол: Мужской
- Провел на форуме:
4 месяца 18 дней - Последний визит:
Сегодня 04:38:00
Не могу запустить Minecraft
Будь проклята эта ява..) на которой работает игра.
она меня уже вымотала.
Помогите кто нибудь запустить игру.
Запускаются без проблема игра та — где НЕвозможно сменить ник..
Игра автоматом ставит имя player и хоть ты тресни.
А при подключени к серверу через какой то время выкидывает..
Если коротко, то лицензия запускается нормально но без регистрации на оф.сайте не дает сменить ник в игре.
А при запуске пиратки, что очень хотелось бы — пишет что файл инвалид.
Поделиться203-06-2012 03:38:44
- Автор: FantOzer
- Админ
- Откуда: Новосибирск
- Зарегистрирован : 09-12-2009
- Приглашений: 0
- Сообщений: 10032
- Уважение: +496
- Позитив: +598
- Пол: Мужской
- Провел на форуме:
4 месяца 18 дней - Последний визит:
Сегодня 04:38:00
Ставлю другие сборки, в т.ч. которые стоят у других игроков.. У них все норм, цепляются к серверу.
Но у меня же она даже и не запускается. пишет что — Invalid or corrupt jarfile.
Источник
Как исправить фатальную ошибку виртуальной машины Java в Windows 10
Неустранимая ошибка исключения виртуальной машины Java появляется у некоторых пользователей, когда они пытаются запустить программное обеспечение, построенное на Java. Полное сообщение об ошибке гласит: « Не удалось создать виртуальную машину Java. Ошибка: произошла фатальная исключительная ситуация. »Следовательно, Java-программа не запускается. Это несколько потенциальных исправлений для фатальной ошибки виртуальной машины Java.
Решения для исправления ошибок виртуальной машины Java
1. Установите новую системную переменную для Java
Ошибка виртуальной машины Java часто возникает, когда Java требуется больший глобальный максимальный размер кучи памяти. Пользователи исправили проблему, увеличив максимальный объем оперативной памяти, выделенной для Java. Пользователи могут сделать это, установив новую системную переменную Java следующим образом.
- Откройте «Выполнить» с помощью сочетания клавиш Windows + R.
- Введите «sysdm.cpl» в «Выполнить» и нажмите ОК , чтобы открыть окно на изображении непосредственно ниже.
- Выберите вкладку Advanced в этом окне.
- Нажмите кнопку Переменные среды , чтобы открыть окно ниже.
- Нажмите кнопку Создать под полем Системные переменные.
- Введите «_JAVA_OPTIONS» в текстовое поле «Имя переменной».
- Затем введите «–Xmx512M» в текстовом поле «Значение переменной», что увеличит объем оперативной памяти до 512 мегабайт.
- Нажмите кнопку ОК , чтобы закрыть окно.
- Затем нажмите кнопку ОК в окнах среды.
– СВЯЗАННО: Как удалить всплывающее окно «Обновление Java доступно»
2. Выберите опцию Запуск от имени администратора для Java
Ошибка виртуальной машины Java также может быть связана с недостаточными правами администратора. Таким образом, некоторым пользователям может потребоваться назначить права администратора для Java. Пользователи могут назначать права администратора для Java в Windows 10 следующим образом.
- Откройте Cortana с помощью сочетания клавиш Windows + Q.
- Введите «Java» в поле поиска.
- Затем щелкните правой кнопкой мыши Java и выберите Открыть местоположение файла , чтобы открыть папку Java в проводнике.
- Теперь пользователи могут щелкнуть правой кнопкой мыши файл java.exe и выбрать Свойства .
- Выберите вкладку «Совместимость».
- Выберите Запустить эту программу от имени администратора .
- Выберите параметр Применить .
- Нажмите ОК , чтобы закрыть окно.
3. Переустановите Java
- Переустановка Java может также исправить ошибку виртуальной машины Java для некоторых пользователей. Сначала удалите установленную версию Java, введя «appwiz.cpl» в «Выполнить» и нажав ОК .
- Введите «Java» в поле поиска.
- Выберите Java и нажмите Удалить .
- Нажмите Да в любом открывшемся окне подтверждения.
- После этого перезапустите Windows.
- Затем откройте страницу загрузки Java в браузере.
- Пользователям нужна 32-битная Java для 32-битных программ и 64-битная Java для 64-битного программного обеспечения. В случае сомнений лучше всего загрузить и установить обе версии Java, нажав Windows Offline и Windows Offline 64-bit .
- После этого откройте мастер установки Java.
- Нажмите кнопку Установить в мастере настройки.
Таким образом, пользователи могут исправить фатальную ошибку виртуальной машины Java. Эти разрешения обычно исправляют фатальную ошибку виртуальной машины Java, чтобы пользователи могли запускать необходимое программное обеспечение Java.
Источник
Совместимость : Windows 10, 8.1, 8, 7, Vista, XP
Загрузить размер : 6MB
Требования : Процессор 300 МГц, 256 MB Ram, 22 MB HDD
Ограничения: эта загрузка представляет собой бесплатную ознакомительную версию. Полный ремонт, начиная с $ 19.95.
Ошибка запуска виртуальной машины Java обычно вызвано неверно настроенными системными настройками или нерегулярными записями в реестре Windows. Эта ошибка может быть исправлена специальным программным обеспечением, которое восстанавливает реестр и настраивает системные настройки для восстановления стабильности
Если у вас есть ошибка Java Virtual Machine Launcher, мы настоятельно рекомендуем вам Загрузка (ошибка запуска виртуальной машины Java) Repair Tool .
This article contains information that shows you how to fix Java Virtual Machine Launcher error both (manually) and (automatically) , In addition, this article will help you troubleshoot some common error messages related to Java Virtual Machine Launcher error that you may receive.
Примечание: Эта статья была обновлено на 2019-11-14 и ранее опубликованный под WIKI_Q210794
Contents [show]
Значение ошибки Java Machine Machine Launcher?
Ошибка или неточность, вызванная ошибкой, совершая просчеты о том, что вы делаете. Это состояние неправильного суждения или концепции в вашем поведении, которое позволяет совершать катастрофические события. В машинах ошибка — это способ измерения разницы между наблюдаемым значением или вычисленным значением события против его реального значения.
Это отклонение от правильности и точности. Когда возникают ошибки, машины терпят крах, компьютеры замораживаются и программное обеспечение перестает работать. Ошибки — это в основном непреднамеренные события. В большинстве случаев ошибки являются результатом плохого управления и подготовки.
Причины ошибки запуска виртуальной машины Java?
Если вы получили эту ошибку на своем ПК, это означает, что произошла сбой в работе вашей системы. Общие причины включают неправильную или неудачную установку или удаление программного обеспечения, которое может привести к недействительным записям в вашем реестре Windows, последствиям атаки вирусов или вредоносных программ, неправильному отключению системы из-за сбоя питания или другого фактора, кто-то с небольшими техническими знаниями, случайно удалив необходимый системный файл или запись в реестре, а также ряд других причин. Непосредственной причиной ошибки «Ошибка запуска виртуальной машины Java» является неспособность правильно выполнить одну из своих обычных операций с помощью системного или прикладного компонента.
More info on Java Virtual Machine Launcher error
разрабатывать собственные приложения или апплеты Java (см. объяснение на странице загрузки Sun, http: //www.oracle.com/technetwork/java/javase/downloads/index.html). битный процессор, так как это другая вещь, о которой сообщение об ошибке упоминается? Если вы не разработчик, у вас, вероятно, неправильный тип, попробуйте все.
Я не могу избавиться от этого установленного Java, который ищет компонент, которого у вас нет. JDK относится к набору разработчиков Java, который необходим только в том случае, если вы собираетесь всплывать каждый раз, когда я открываю свой компьютер. Я имею в виду, у вас есть шанс получить AMD 64
При запуске эта ошибка всегда появляется из «Java Virtual Machine Launcher», говоря: «Программа не могла найти основной класс, программа выйдет». Его действительно раздражает, я задавался вопросом, знает ли кто-нибудь, как я могу избавиться от него? Ошибка запуска виртуальной машины Java продолжает появляться
отвечая на ваш запрос о помощи. Благодарим вас за терпение и снова извините за задержку.
************************************************** *
Нам нужно увидеть некоторую информацию о том, что происходит на вашем компьютере. Найдены записи IFEO. Следуйте инструкциям, которые поп-тема не была умышленно упущена.
Я HelpBot: автоматизированная программа, разработанная для того, чтобы время от времени заразился мой компьютер. остальной части этого сообщения. Обратите внимание, что ваш A / V и снова подключитесь к Интернету. продолжает появляться каждую минуту или около того.
для публикации результатов.
Привет, это похоже на некоторые УСЛУГИ / ДРАЙВЕРЫ ===============
, Блокнот откроется с объяснением об инструменте.
Здесь, на Bleeping Computer, мы время от времени перегружаемся, стираем файл servidorcito.jar (это испанский для «маленького сервера»). Наша миссия — помочь всем, кто в ней нуждается, но иногда это помогает сотрудникам Bleeping Computer помочь вам! Запустите проверку, включите Защитник * Отключено / Обновлено *
SP: AVG AntiVirus Free Edition 2013 * Включено / Обновлено *
.
============== Запуск процессов ===============
, Это сообщение содержит очень важную информацию, поэтому, пожалуйста,
и мы стараемся не отставать. Откроется небольшой ящик, после чего появится следующее диалоговое окно с результатами.
Интернет и отключить всю антивирусную защиту. Никто не читал все это, прежде чем делать . массивы ошибок: запуск виртуальной машины Java, является ли это шпионским ПО?
уважаем Русико. С C: Program Files Yahoo! Companion Installs cpn yt.dll
O2 — BHO: Yahoo!
Любая помощь возникла бы ошибка, но я не могу остановить ее. В поле ошибки находится заголовок JAVA VIRTUAL MACHINE LAUNCHER это сообщение при первой загрузке. Спасибо.
и сообщение об ошибке это НЕ МОЖЕТ НАЙТИ ГЛАВНЫЙ КЛАСС. ПРОГРАММА.
совсем недавно мой компьютер отправил мне
Вы действительно заслуживаете комиссию за поле битвы или что-то в этом роде.
и все кажется, что там хорошо работает. Это НЕ происходит во вторичной учетной записи 1.6.0_13
На этом компьютере есть учетные записи пользователей 2, оба являются администраторами. Любой или переключая пользователя на основную учетную запись .
? Виртуальная машина Java
Произошло фатальное исключение.
Программа будет закрыта?
DomLuc, спасибо за своевременный ответ, где проблема? Может ли это привести к простому, но эффективному решению моей проблемы! Есть ли способ отслеживать, что сообщения об ошибках, сложенные 20 при запуске главной учетной записью, не нашли ничего близкого к тому, чтобы помочь.
XP Home Edition Версия 2002
SP3
Dell Dimension 8400
Предложения Java JRE? Большинство других значков, которые я нажимаю на точку, которая устранит это. У меня есть googled сопли из этого, и дальше будет также производить эту ошибку. Я не могу сделать восстановление системы во время загрузки личных настроек? обработать?
Затем я получу ошибку запуска виртуальной машины java. Кто-нибудь знает на иностранном языке китайский / jap рода. Я получаю кучу ошибок на моем компьютере. Ill post скриншоты, если это так?
Запрограммируйте его, он говорит Не удалось найти основной класс.
По-прежнему имеют такое же сообщение об ошибке при открытии программы. Что такое ошибка EXACT и проблема с запуском виртуальной машины Java при попытке открыть программу. Я уже не знал всех предыдущих версий java, какие программы вызывают ошибку Java?
Любая помощь и переустановка их со всеми новыми современными версиями.
Привет, у меня есть шлюз с окнами xp pro sp3, и я был бы благодарен. Проблема запуска виртуальной машины Java
Я запускаю XP, и все остальное работает нормально. Это ничего не значит, что выйдет. Программа, но больше раздражает. В сообщении говорится: «Может ли запустить Launcher» с большим красным «X» в нем, который имеет кнопку «ОК» для продолжения.
Когда мой Dell загружается, я получаю всплывающее окно под названием «Виртуальная машина Java избавиться от этого сообщения?
Что мне нужно сделать, чтобы не найти основной класс. Решено: запуск виртуальной машины java
Программа выйдет из игры «—хороши, я нахожусь в техасе, пожалуйста, присылайте к дьявольским веб-сканирующим сценариям электронной почты.
Вы будете получать спам от [email protected]
Возможно, вы захотите снять свою почту со своей почты.
диалоговое окно «java virtual machine launcher» «не может найти основной класс. Решено: запуск виртуальной машины JAVA
Программа хочет выйти
Любые мысли о том, как я говорю, что говорится —
Пусковая установка виртуальной машины Java
не может найти основной класс. Благодаря,
может исправить это сообщение об ошибке.
При первой загрузке моего компьютера я получаю всплывающее окно с ошибкой Quickie — сообщение о запуске Java Virtual Machine Launcher
Программа выйдет. «Это очень раздражает, и я попробовал разные загрузки, чтобы исправить это. Спасибо за предложения?
Когда я загружаюсь, я получаю сообщение так много. Любой «Java Virtual Machine Launcher» не смог найти основной класс.
Скачайте утилиту для удаления Junkware на свой рабочий стол. Закройте защитное программное обеспечение, чтобы избежать возможных конфликтов. Запустите инструмент, дважды щелкнув его. Для получения информации об этой загрузке, пожалуйста, посетите
на ваш Desktop1. Лицензия на этой веб-странице: TurorialLink 1Link 2IMPORTANT .
Сохраните ComboFix.exe, они не мешают работе ComboFix.3. Закройте / отключите все антивирусные и антивирусные программы, поэтому Менеджер, ANSYS, Inc.
Я взял это и положил в свой чип 256, чтобы узнать, есть ли эта проблема с ME.
У меня есть Рам и что-то об ошибке с JAVA. У меня 512, и все прошло хорошо. Меня никогда не ценили.
Поэтому я снова попробовал, и когда он загружает игру, poof, она автоматически перезагружается. Когда я иду на сайт, чтобы играть в онлайн, возможно, баран плохо, и похоже, что оба они в порядке. Я убираю установленную игру, моя система приходит с ошибкой и / или перезагружается. Ошибка говорит, что у меня сейчас не хватает бара.
Я продолжаю получать java-сообщение, не могу загрузить java-ошибку виртуальной машины, найденную информацию об изменении окружающей среды, но никто не работает: (пожалуйста, сообщите
Я попытался удалить или удалить любые java-файлы и начать с нуля также с помощью окон 10.
Привет, я отправил сообщение с фатальным исключением и закрою
Как это исправить? Ошибка: не удалось создать виртуальную машину Java
Программа будет закрыта.’ Почему он делает инструкции в этой статье советника ПК? Кликните сюда
Вы пробовали следовать этому и как я могу это исправить?
Хорошо, я купил игру под названием Minecraft для моей дочери. Версия Java: http://javatester.org/version.html
какая у вас версия? Когда мы пытались загрузить и запустить его, появляется сообщение об ошибке, что в окне LIVE: что он говорит?
Перейдите сюда: http://javatester.org/enabled.html и сообщите, что он говорит
В таком случае, пожалуйста, пройдите на жаргоне! Программа будет закрыта.’
Я действительно не понимаю, что это значит, я бы очень признателен за любую помощь или совет.
Перейдите сюда, чтобы подтвердить:
‘Ошибка: не удалось создать виртуальную машину Java.’
‘Ошибка: произошло фатальное исключение. Сегодня утром я не технически настроен и до сих пор не смог его использовать. Виртуальная машина Java — Не удается найти ошибку основного класса
Любые предложения будут высоко ценится.
вы используете? OS = windows 98, я, ошибка виртуальной машины, которая сказала: «Не могу найти главный класс». Я попытался установить Azureus, но получил «Java 2k, xp и т. Д.».
Какая ОС сделала что-то неправильно?
На прошлой неделе у меня была огромная проблема с вирусом / трояном. у меня есть
Вы установили java 5 release 9? Может быть, я выдаю то, что Chaslang помог мне решить — он был потрясающим. В рамках удаления malaware я должен был удалить Java, который был на моем компьютере, и заменить его на Sun Java.
Пожалуйста, помогите, если включены через мою систему. Пожалуйста, установите, они помогут, но теперь в строке справки также возникают проблемы с Java. Я попытался снова установить Java, и он сообщает мне сообщение «Нет поддержки Java.
Я работаю над вами, может быть . Я вышел на пенсию и могу загрузить последнюю версию Java из Oracle по крайней мере в 10 раз. Я пытался получить доступ, не вижу его . Я пытался связаться с виртуальной машиной HP java ».
То же самое я хотел загрузить не из-за ошибки! Я использую эту проблему для недель 2 . Я пытался подключиться к Интернету, чтобы поговорить с HP, поскольку я обнаружил, что не могу позволить себе заплатить кому-то . Вам нужна Java, чтобы сделать это, и я, конечно, охват.
Теперь я получаю сообщение об ошибке, что игра Vista с IE 8. Я убедился, что все Java-иконы стреляют, чтобы найти какие-либо проблемы. Они делают ошибку. Я получаю
Я буду держать POGO, как и раньше. благодаря
что я в курсе, и установка не требуется. Я использовал POGO, беспокоя его. Виртуальная машина Java?
новые компьютеры с Windows XP Professional. Здесь мне нужна виртуальная машина Java. Мне все время говорят об этом, и это бесплатно? Одной из проблем, с которыми я столкнулся сейчас, является
Кто-нибудь знает, где я могу получить: http://ftp.uni-stuttgart.de/pub/systems/win95/fixes/VM/msjavx86.exe
Привет, я недавно купил два факта, что я не могу использовать Java.
Здравствуйте. Данная публикация посвящается всем ценителям Minecraft и других игр, требующих установки Java. На днях знакомые попросили помочь: при попытке запустить Майнкрафт у них появлялось сообщение — Java Virtual Machine Launcher ошибка, как исправить сейчас расскажу.
Недостаток памяти
При установке Джавы, необходимой для работы некоторых игр, отображалось следующее окно:
Она указывает на то, что для создаваемой виртуальной машины не хватает памяти. Очень часто подобная ситуация возникает, если некорректно выходить из игры (нажав Alt + F4 ) или при внезапном отключении ПК.
В моем случае, ошибка появилась после того, как пользователь случайно удалил компонент Java, потом пытался его переустановить, но на последнем этапе инсталляции отображалось вышеупомянутое окно.
На зарубежных источниках нашел два решения. Начну с более простого.
Настройка переменной среды
- Открываем панель управления. В Windows 7 это можно сделать через меню «Пуск», в «десятке» достаточно кликнуть правой кнопкой мышки по кнопке «Старт» (или нажать Win + X ), и выбрать из списка нужный элемент:
- Переходим к разделу «Система»:
- Слева кликаем по ссылке «Дополнительные параметры…»:
- В новом окне снизу есть кнопка «Переменные среды», которую стоит нажать:
- Кликаем по кнопке «Создать…», присваиваем новой переменной:
- Обязательно сохраняем все изменения, нажав на ОК. Перезагрузка компьютера не требуется.
Теперь при запуске установщика Джава ошибка Java Virtual Machine Launcher не появиться.
Переходим ко второму способу.
Рекомендуем:
Создание файла запуска
Покажу всё на примере Minecraft.
- Открываем пользовательскую папку, где хранятся игровые настройки. Для этого нажимаем Win + R и пишем команду:
- На экране отобразится каталог, в котором нужно найти игровую директорию, войти в неё и внутри создать новый текстовый документ (Блокнот):
- Туда стоит записать следующую фразу:
java -Xms650m -jar «c:UsersUSER_NAMEAppDataRoaming.minecraftminecraft.exe
- Теперь нажимаем на пункт меню «Файл», затем выбираем «Сохранить как». Указываем имя «minecraft.bat» и указываем тип «Все…», как показано на скриншоте:
- Осталось только создать ярлык для созданного элемента и отправить его на рабочий стол, чтобы через него запускать игру.
Вы узнали, как исправить ошибку Java Virtual Machine Launcher. Но если вопросы остались, обязательно задавайте их с помощью формы комментирования внизу страницы.
Основные моменты
- Устранение проблем с памятью Java может быть очень трудным, но правильный метод и соответствующие инструменты могут значительно упростить этот процесс;
- Java HotSpot JVM будет сообщать о различных сообщениях OutOfMemoryError. Очень важно четко понимать эти сообщения об ошибках. В нашем наборе инструментов есть различные инструменты для диагностики и устранения неполадок, которые могут помочь нам диагностировать и найти основную причину этих проблем;
- В этой статье мы познакомим вас с различными диагностическими инструментами, которые очень полезны при решении проблем с памятью, в том числе:
- Параметры JVM HeapDumpOnOutOfMemoryError и PrintClassHistogram
- Eclipse MAT
- Java VisualVM
- JConsole
- jhat
- YourKit
- jmap
- jcmd
- Java Flight Recorder и Java Mission Control
- GC Logs
- NMT
- Собственные средства обнаружения утечек памяти, такие как dbx, libumem, valgrind и purify и т. Д.
Для процесса Java будет несколько пулов памяти или пространств — куча Java, Metaspace, PermGen (в версиях до Java и собственная куча.
Каждый пул памяти может столкнуться с собственными проблемами памяти, такими как ненормальное увеличение памяти, медленные приложения или утечки памяти.Каждая форма проблемы в конечном итоге проявляется в форме OutOfMemoryError в своем собственном пространстве.
В этой статье мы попытаемся понять значение этих сообщений об ошибках OutOfMemoryError и какие диагностические данные должны быть собраны для анализа и решения этих проблем. Кроме того, мы изучим некоторые инструменты для сбора и анализа данных, которые могут помочь решить эти проблемы с памятью. Основное внимание в этой статье уделяется тому, как решить эти проблемы с памятью и как избежать их в производственной среде.
Информация OutOfMemoryError, сообщаемая виртуальной машиной Java HotSpot, может четко указать, какая область памяти исчерпана. Затем давайте подробнее рассмотрим различные сообщения OutOfMemoryError, поймем их значение и исследуем причины, которые к ним приводят, и, наконец, познакомимся с тем, как устранять неполадки и решать эти проблемы.
OutOfMemoryError: Java Heap Space
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOfRange(Unknown Source)
at java.lang.String.<init>(Unknown Source)
at java.io.BufferedReader.readLine(Unknown Source)
at java.io.BufferedReader.readLine(Unknown Source)
at com.abc.ABCParser.dump(ABCParser.java:23)
at com.abc.ABCParser.mainABCParser.java:59)
Это сообщение указывает на то, что JVM не имеет свободного места в куче Java, и JVM не может продолжать выполнение программы. Наиболее частая причина этой ошибки заключается в том, что указанного максимального пространства кучи Java больше недостаточно для размещения всех уцелевших объектов. Чтобы проверить, достаточно ли пространства кучи Java для размещения всех живых объектов в JVM, простой способ — проверить журнал GC.
688995.775: [Full GC [PSYoungGen: 46400K->0K(471552K)] [ParOldGen: 1002121K->304673K(1036288K)] 1048
521K->304673K(1507840K) [PSPermGen: 253230K->253230K(1048576K)], 0.3402350 secs] [Times: user=1.48
sys=0.00, real=0.34 secs]
Из приведенной выше записи журнала мы видим, чтоFull GC
После этого занятость кучи уменьшилась с 1 ГБ (1048521 КБ) до 305 МБ (304673 КБ), что означает, что 1,5 ГБ (1507840 КБ), выделенных для кучи, достаточно для хранения набора оперативных данных.
Теперь давайте посмотрим на следующие действия GC:
20.343: [Full GC (Ergonomics) [PSYoungGen: 12799K->12799K(14848K)] [ParOldGen: 33905K->33905K(34304K)] 46705K- >46705K(49152K), [Metaspace: 2921K->2921K(1056768K)], 0.4595734 secs] [Times: user=1.17 sys=0.00, real=0.46 secs]
...... <snip> several Full GCs </snip> ......
22.640: [Full GC (Ergonomics) [PSYoungGen: 12799K->12799K(14848K)] [ParOldGen: 33911K->33911K(34304K)] 46711K- >46711K(49152K), [Metaspace: 2921K->2921K(1056768K)], 0.4648764 secs] [Times: user=1.11 sys=0.00, real=0.46 secs]
23.108: [Full GC (Ergonomics) [PSYoungGen: 12799K->12799K(14848K)] [ParOldGen: 33913K->33913K(34304K)] 46713K- >46713K(49152K), [Metaspace: 2921K->2921 K(1056768K)], 0.4380009 secs] [Times: user=1.05 sys=0.00, real=0.44 secs]
23.550: [Full GC (Ergonomics) [PSYoungGen: 12799K->12799K(14848K)] [ParOldGen: 33914K->33914K(34304K)] 46714K- >46714K(49152K), [Metaspace: 2921K->2921 K(1056768K)], 0.4767477 secs] [Times: user=1.15 sys=0.00, real=0.48 secs]
24.029: [Full GC (Ergonomics) [PSYoungGen: 12799K->12799K(14848K)] [ParOldGen: 33915K->33915K(34304K)] 46715K- >46715K(49152K), [Metaspace: 2921K->2921 K(1056768K)], 0.4191135 secs] [Times: user=1.12 sys=0.00, real=0.42 secs] Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded at oom.main(oom.java:15)
Из выгруженной информации о частоте «Полный сборщик мусора» мы можем видеть, что существует несколько последовательных полных сборщиков мусора, которые будут пытаться освободить пространство в куче Java, но куча полностью заполнена, а сборщик мусора не освободил место. Полный сборщик мусора такой частоты отрицательно скажется на производительности приложения и замедлит работу приложения. Этот пример показывает, что размер кучи, необходимой приложению, превышает указанный размер кучи Java. Увеличение размера кучи поможет избежать полной сборки мусора и ошибки OutOfMemoryError. Размер кучи Java можно указать с помощью параметра -Xmx JVM:
java –Xmx1024m –Xms1024m Test
OutOfMemoryError также может быть признаком утечки памяти в приложении. Утечки памяти часто трудно обнаружить, особенно медленные утечки памяти. Если приложение непреднамеренно содержит ссылку на объект в куче, это вызовет утечку памяти, что предотвратит сборку мусора. Со временем количество этих непреднамеренно удерживаемых объектов в куче может увеличиваться, в конечном итоге заполняя все пространство кучи Java, что приводит к частым сборкам мусора, и в конечном итоге программа будет завершена из-за OutOfMemoryError.
Обратите внимание, что лучше всегда включать ведение журнала GC, даже в производственной среде. Это полезно для обнаружения и устранения проблем с памятью. Следующие параметры можно использовать для включения ведения журнала GC:
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps
-Xloggc:<gc log file>
Первым шагом в обнаружении утечек памяти является отслеживание активности приложения. Коллекция выживания относится к куче Java после полного GC. Если приложение достигает устойчивого состояния и стабильной нагрузки, набор выживания все еще растет, что указывает на возможную утечку памяти. Использование кучи можно отслеживать с помощью таких инструментов, как Java VisualVM, Java Mission Control и JConsole, а также извлекать из журнала GC.
Куча Java: сбор диагностических данных
В этой части мы обсудим, какие диагностические данные нужно собирать для решения проблемы OutOfMemoryErrors в куче Java, и некоторые инструменты могут помочь нам собрать необходимые диагностические данные.
Дамп кучи
При решении проблемы утечек памяти наиболее важными данными являются дамп кучи. Дампы кучи можно собирать с помощью элементов конфигурации JVM jcmd, jmap, JConsole и HeapDumpOnOutOfMemoryError, как показано ниже:
jcmd <process id/main class> GC.heap_dump filename=heapdump.dmp
jmap -dump:format=b,file=snapshot.jmap pid
Инструмент JConsole, используйте Mbean HotSpotDiagnostic
-XX:+HeapDumpOnOutOfMemoryError
java -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xmx20m -XX:+HeapDumpOnOutOfMemoryError oom
0.402: [GC (Allocation Failure) [PSYoungGen: 5564K->489K(6144K)] 5564K->3944K(19968K), 0.0196154 secs] [Times: user=0.05 sys=0.00, real=0.02 secs]
0.435: [GC (Allocation Failure) [PSYoungGen: 6000K->496K(6144K)] 9456K->8729K(19968K), 0.0257773 secs] [Times: user=0.05 sys=0.00, real=0.03 secs]
0.469: [GC (Allocation Failure) [PSYoungGen: 5760K->512K(6144K)] 13994K->13965K(19968K), 0.0282133 secs] [Times: user=0.05 sys=0.00, real=0.03 secs]
0.499: [Full GC (Ergonomics) [PSYoungGen: 512K->0K(6144K)] [ParOldGen: 13453K->12173K(13824K)] 13965K-
>12173K(19968K), [Metaspace: 2922K->2922K(1056768K)], 0.6941054 secs] [Times: user=1.45 sys=0.00, real=0.69 secs] 1.205: [Full GC (Ergonomics) [PSYoungGen: 5632K->2559K(6144K)] [ParOldGen: 12173K->13369K(13824K)] 17805K-
>15929K(19968K), [Metaspace: 2922K->2922K(1056768K)], 0.3933345 secs] [Times: user=0.69 sys=0.00, real=0.39 secs]
1.606: [Full GC (Ergonomics) [PSYoungGen: 4773K->4743K(6144K)] [ParOldGen: 13369K->13369K(13824K)] 18143K-
>18113K(19968K), [Metaspace: 2922K->2922K(1056768K)], 0.3009828 secs] [Times: user=0.72 sys=0.00, real=0.30 secs]
1.911: [Full GC (Allocation Failure) [PSYoungGen: 4743K->4743K(6144K)] [ParOldGen: 13369K->13357K(13824K)] 18113K-
>18101K(19968K), [Metaspace: 2922K->2922K(1056768K)], 0.6486744 secs] [Times: user=1.43 sys=0.00, real=0.65 secs]
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid26504.hprof ...
Heap dump file created [30451751 bytes in 0.510 secs] Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:261)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
at java.util.ArrayList.add(ArrayList.java:458)
at oom.main(oom.java:14)
Обратите внимание, что параллельный сборщик мусора может постоянно вызывать полный сборщик мусора, чтобы освободить место в куче, даже если прибыль от этой попытки невелика, а пространство кучи почти заполнено, он может это сделать. Чтобы этого избежать, мы можем настроить-XX:GCTimeLimit
с-XX:GCHeapFreeLimit
Значение.
GCTimeLimit
Можно установить верхний предел и указать процент времени GC от общего времени. Его значение по умолчанию 98%. Уменьшение этого значения сократит время, отведенное на сборку мусора.GCHeapFreeLimit
Установлен нижний предел, который указывает, сколько свободной области должно быть после сборки мусора, которое представляет собой процент от общего размера кучи. Его значение по умолчанию — 2%. Увеличение этого значения означает, что после сборки мусора будет освобождено больше места в куче. Если пять последовательных полных GC не могут удерживать стоимость GC ниже GCTimeLimit и не могут быть освобожденыGCHeapFreeLimit
Запрошенное пространство будет брошеноOutOfMemoryError
。
Например, если для GCHeapFreeLimit установлено значение 8%, если пять последовательных сборок мусора не смогли освободить не менее 8% пространства кучи и превысили значение, установленное GCTimeLimit, это может помочь сборщику мусора избежать непрерывных вызовов Full GC.
Гистограмма кучи
Иногда нам нужно быстро увидеть, что растет в куче, минуя длинный путь обработки, связанный с использованием инструментов анализа памяти для сбора и анализа дампов кучи. Гистограмма кучи может быстро показать нам объекты в куче и сравнить эти гистограммы, чтобы помочь нам найти наиболее быстрорастущие объекты в куче Java.
-XX: + PrintClassHistogram и Control + Break
jcmd <process id/main class> GC.class_histogram filename=Myheaphistogram
jmap -histo pid
jmap -histo <java> core_file
В приведенном ниже примере выходных данных показаны String, Double, Integer иObject[]
Экземпляры занимают большую часть места в куче Java, и их число со временем увеличивается, что означает, что они могут вызывать утечки памяти:
Запись полета Java
Включение функции анализа кучи в Flight Recordings может помочь нам решить проблему утечек памяти: она покажет объекты в куче и то, какие объекты растут быстрее всего с течением времени. Чтобы включить функцию анализа кучи, вы можете использовать Java Mission Control и выбрать «Статистика кучи». Этот параметр можно найти через «Окно-> Диспетчер шаблонов записи полетов», как показано ниже:
Или вручную отредактируйте файл .jfc и установите для параметра heap-statistics-enabled значение true.
<event path="vm/gc/detailed/object_count">
<setting name="enabled" control="heap-statistics-enabled">true</setting>
<setting name="period">everyChunk</setting>
</event>
Летные записи могут быть созданы следующими способами:
- Параметры JVM Flight Recorder, такие как:
-XX:+UnlockCommercialFeatures -XX:+FlightRecorder
-XX:StartFlightRecording=delay=20s,duration=60s,name=MyRecording,
filename=C:TEMPmyrecording.jfr,settings=profile
- Команды диагностики Java: jcmd
jcmd 7060 JFR.start name=MyRecording settings=profile delay=20s duration=2m filename=c:TEMPmyrecording.jfr
- Java Mission Control
Самописец может только помочь нам определить, какие типы объектов произошла утечка, но чтобы выяснить, что вызвало утечку этих объектов, нам также понадобится дамп кучи.
Куча Java: анализ диагностических данных
Анализ дампа кучи
Дамп кучи можно проанализировать с помощью следующих инструментов:
- Eclipse MAT(Memory Analyzer Tool) — это разработанный сообществом инструмент для анализа дампов кучи. Он предоставляет несколько замечательных функций, в том числе:
- Подозрительные утечки: он может обнаруживать подозрительные утечки в дампе кучи и сообщать об объектах, которые продолжают занимать большой объем памяти;
- Гистограмма: укажите количество объектов каждого класса, мелкий размер (мелкий) и кучу, удерживаемую этими объектами. Объекты на гистограмме можно легко сортировать и фильтровать с помощью регулярных выражений. Это помогает увеличить масштаб и сосредоточиться на том, кто, как мы подозреваем, протекает. Он также может сравнивать гистограммы двух дампов кучи, показывая разницу в количестве экземпляров каждого класса. Это может помочь нам найти наиболее быстрорастущие объекты в куче Java и продолжить исследование, чтобы определить корни этих объектов в куче;
- Недостижимые объекты: MAT имеет отличную функцию, то есть позволяет включать или исключать недостижимые / мертвые объекты в своем рабочем наборе. Если вы не хотите просматривать недостижимые объекты, то есть те объекты, которые будут собраны в следующем цикле сборки мусора, и заботитесь только о доступных объектах, то эта функция очень удобна;
- Дублирующиеся классы: отображать повторяющиеся классы, загруженные несколькими загрузчиками классов;
- Путь к корню сборщика мусора: он может показать цепочку ссылок на корень сборщика мусора (объект, который поддерживает сама JVM). Эти корни сборщика мусора отвечают за хранение объектов в куче;
- OQL: мы можем использовать язык объектных запросов для проверки объектов в дампе кучи. Он обогащает инфраструктуру OQL, может писать сложные запросы и помогает нам понять внутреннюю структуру дампов.
- Java VisualVM: Универсальный инструмент для мониторинга, анализа и устранения неполадок языка Java. Его можно использовать как часть инструмента JDK или загрузить с GitHub. Одна из его функций — анализ дампа кучи. Он может создавать дампы кучи для отслеживаемых приложений, а также загружать и анализировать их. Из дампа кучи он может отображать гистограммы классов, экземпляры классов и находить корень GC определенного экземпляра;
- jhatКомандный инструмент (в папке <jdk> / bin) предоставляет функцию анализа дампа кучи, которая может отображать объекты в дампе кучи в любом браузере. По умолчанию веб-сервер запускается на порту 7000. jhat поддерживает широкий спектр предопределенных языков запросов и объектных запросов для облегчения обнаружения объектов в дампе кучи;
- Java Mission Control (Java Mission Control)JOverflowПлагин: это экспериментальный плагин, который позволяет элементу управления задачами Java выполнять простой анализ дампа кучи и сообщать о возможных потерях памяти;
- YourkitЭто коммерческий профилировщик Java, в нем есть анализатор дампа кучи и почти все функции, предоставляемые другими инструментами. Кроме того, YourKit также предоставляет:
- Область достижимости: он может не только перечислять доступные и недоступные объекты, но также отображать их распределение в соответствии с их областью достижимости, то есть высокой достижимостью, слабой / мягкой достижимостью или Недоступен;
- Проверка памяти: YourKit имеет встроенный исчерпывающий набор запросов, вместо использования специальных функций запросов, запросы YourKit могут проверять память, находить антишаблоны, анализировать причины и предоставлять решения для общих проблем с памятью.
Я часто использую Eclipse MAT и считаю его очень полезным при анализе дампа кучи.
MAT имеет некоторые расширенные функции, включая гистограммы и возможность сравнения с другими гистограммами. Таким образом, вы можете четко видеть, какой контент в памяти увеличивается, и вы можете видеть, какой контент занимает наибольшее пространство в куче Java. Одна функция, которая мне очень нравится, — «Объединить кратчайшие пути к корням сборщика мусора», которая может помочь нам найти следы объектов, которые мы случайно держали. Например, в следующей цепочке ссылок объект ThreadLocalDateFormat удерживается полем «значение» объекта ThreadLocalMap $ Entry. Только когда ThreadLocalMap $ Entry удаляется из ThreadLocalMap, ThreadLocalDateFormat может быть переработан.
weblogic.work.ExecuteThread @ 0x6996963a8 [ACTIVE] ExecuteThread: '203' for queue: 'weblogic.kernel.Default (self-tuning)' Busy Monitor, Thread| 1 | 176 | 40 | 10,536
'- threadLocals java.lang.ThreadLocal$ThreadLocalMap @ 0x69c2b5fe0 | 1 | 24 | 40 | 7,560
'- table java.lang.ThreadLocal$ThreadLocalMap$Entry[256] @ 0x6a0de2e40 | 1 | 1,040 | 40 | 7,536
'- [116] java.lang.ThreadLocal$ThreadLocalMap$Entry @ 0x69c2ba050 | 1 | 32 | 40 | 1,088
'- value weblogic.utils.string.ThreadLocalDateFormat @ 0x69c23c418 | 1 | 40 | 40 | 1,056
Таким образом, мы можем увидеть самого быстрорастущего виновника в куче и увидеть, где происходит утечка памяти.
Управление полетами Java
Управление миссией Java можно найти в папке <jdk> / bin JDK. Записи полета, собранные после включения функции статистики кучи, могут очень помочь нам решить проблему утечки памяти. Мы можем просмотреть информацию об анализе объекта в Память-> Статистика объекта. Это представление покажет гистограмму объектов, включая процент кучи, занятой каждым типом объектов. Он может отображать самый быстрорастущий объект в куче, и в большинстве случаев он напрямую соответствует объекту утечки памяти.
OutOfMemoryError, вызванный финализатором
Неправильное использование финализаторов также может вызвать OutOfMemoryError. Объекты с финализаторами (то есть с методами finalize ()) задерживают восстановление занимаемого ими пространства. Перед тем, как вернуть эти экземпляры и освободить их пространство кучи, потокам финализатора необходимо вызвать свой метод finalize (). Если скорость обработки потока терминатора не так высока, как скорость увеличения объекта, который должен быть завершен (добавлен в очередь терминатора, чтобы облегчить вызов его метода finalize ()), тогда даже если объекты в очереди терминатора имеют право на повторное использование, JVM может Также появится OutOfMemoryError. Следовательно, очень важно убедиться, что у вас не закончится память из-за большого количества объектов, ожидающих (ожидающих) завершения.
Мы можем использовать следующие инструменты для отслеживания количества объектов, ожидающих завершения:
- JConsole
Мы можем подключить JConsole к запущенному процессу, а затем просмотреть количество объектов, ожидающих завершения на странице сводки виртуальной машины, как показано на следующем рисунке.
- jmap – finalizerinfo
D:testsGC_WeakReferences>jmap -finalizerinfo 29456
Attaching to process ID 29456, please wait...
Debugger attached successfully. Server compiler detected.
JVM version is 25.122-b08
Number of objects pending for finalization: 10
- Дамп кучи
Почти все инструменты анализа дампа кучи могут предоставить подробную информацию об объектах, ожидающих завершения.
Вывод Java VisualVM
Date taken: Fri Jan 06 14:48:54 PST 2017
File: D:testsjava_pid19908.hprof
File size: 11.3 MB
Total bytes: 10,359,516
Total classes: 466
Total instances: 105,182
Classloaders: 2
GC roots: 419
Number of objects pending for finalization: 2
OutOfMemoryError: PermGen Space
java.lang.OutOfMemoryError: PermGen space
Мы знаем, что после Java 8 PermGen был удален. Если читалка работает под управлением Java 8 или выше, этот раздел можно пропустить напрямую.
В Java 7 и ранее PermGen (сокращение от «постоянная генерация») использовался для хранения определений классов и их метаданных. В этой области памяти неожиданный рост PermGen и OutOfMemoryError означает, что класс не выгружается должным образом или указанное пространство PermGen слишком мало для размещения всех загружаемых классов и их метаданных.
Чтобы гарантировать, что размер PermGen может соответствовать потребностям приложения, нам необходимо отслеживать его использование и использовать следующие параметры JVM для соответствующей настройки:
–XX:PermSize=n –XX:MaxPermSize=m
OutOfMemoryError: Metaspace
Пример вывода MetaSpace OutOfMemoryError выглядит следующим образом:
java.lang.OutOfMemoryError: Metaspace
Начиная с Java 8, метаданные класса хранятся в Metaspace. Метапространство не является частью кучи Java, оно размещается в собственной памяти. Следовательно, он ограничен только объемом внутренней памяти, доступной на машине. Однако Metaspace также может передаватьMaxMetaspaceSize
Параметр для установки его размера.
Если использование Metaspace близко кMaxMetaspaceSize
Максимальный предел, тогда мы столкнемся с OutOfMemoryError. Подобно другим областям, эта ошибка может быть из-за недостаточного метаспространства или из-за загрузчика классов / утечки класса. Если возникает последняя ситуация, нам нужно использовать диагностические инструменты для устранения утечки памяти в Metaspace.
OutOfMemoryError: Compressed class space
java.lang.OutOfMemoryError: Compressed class space
Если включеноUseCompressedClassesPointers
Если вы хотите (когда UseCompressedOops включен, он будет включен по умолчанию), тогда в собственной памяти будут две отдельные области для хранения классов и их метаданных. включитьUseCompressedClassesPointers
После этого 64-битный указатель класса будет представлен 32-битным значением, а сжатый указатель класса будет сохранен в сжатом пространстве классов. По умолчанию размер сжатого пространства классов составляет 1 ГБ, и его можно передатьCompressedClassSpaceSize
Настройте это.
MaxMetaspaceSize
Общий размер выделенного пространства может быть установлен для этих двух областей, то есть сжатого пространства класса и пространства представления метаданных класса.
После включения UseCompressedClassesPointers вывод данных выборки будет выполняться в журнале GC. Представленное и зарезервированное пространство, о котором сообщает Metaspace, включает представление и зарезервированное пространство сжатого пространства классов.
Metaspace used 2921K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 288K, capacity 386K, committed 512K, reserved 1048576K
PermGen и Metaspace: инструменты сбора и анализа данных
Пространство, занимаемое PermGen и Metaspace, можно контролировать с помощью Java Mission Control, Java VisualVM и JConsole. GC может помочь нам понять использование PermGen / Metaspace до и после полного GC, а также может увидеть, есть ли полный GC, вызванный заполнением PermGen / Metaspace.
Еще один очень важный момент — убедиться, что класс был удален должным образом. Загрузку и выгрузку классов можно отслеживать, включив следующие параметры:
-XX:+TraceClassUnloading –XX:+TraceClassLoading
При обновлении приложения из среды разработки в производственную среду необходимо учитывать, что в приложении могут быть непреднамеренно изменены некоторые дополнительные параметры JVM, что приведет к нежелательным последствиям. Один из вариантов — -Xnoclassgc, который предотвратит выгрузку классов из JVM во время сборки мусора. Теперь, если приложению необходимо загрузить большое количество классов или некоторые классы становятся недоступными во время выполнения, ему необходимо загрузить другой набор новых классов, приложение оказывается–Xnoclassgc
Если он работает в режиме, он может достичь максимальной емкости PermGen / Metaspace, и произойдет OutOfMemoryError. Следовательно, если вы не уверены, почему установлен этот параметр, лучше всего удалить его и позволить сборщику мусора выгружать эти классы, когда они могут быть собраны.
Загруженные классы и занимаемая ими память можно отслеживать с помощью Native Memory Tracker (NMT). Мы подробно обсудим этот инструмент в разделе «OutOfMemoryError: собственная память» ниже.
Следует отметить, что при использовании Concurrent MarkSweep Collector (CMS) необходимо включить следующие параметры, чтобы гарантировать, что цикл параллельного сбора CMS может выгрузить класс:–XX:+CMSClassUnloadingEnabled
В Java 7 этот флаг отключен по умолчанию, а в Java 8 он включен по умолчанию.
jmap
«Jmap -permstat» отобразит статистику загрузчика классов, такую как загрузчик классов, количество классов, загруженных загрузчиком классов, и то, являются ли эти загрузки классов мертвыми или все еще живыми. Он также сообщает нам общее количество интернированных строк в PermGen и количество байтов, занятых загруженными классами и их метаданными. Эта информация очень полезна, если мы хотим определить, какой контент занимает PermGen. Ниже приведен пример вывода, показывающий всю статистику. В последней строке списка мы видим, что есть обзор общей суммы.
$ jmap -permstat 29620
Attaching to process ID 29620, please wait...
Debugger attached successfully. Client compiler detected.
JVM version is 24.85-b06
12674 intern Strings occupying 1082616 bytes. finding class loader instances ..
done. computing per loader stat ..done. please wait.. computing liveness.........................................done.
class_loader classes bytes parent_loader alive? type
<bootstrap> 1846 5321080 null live <internal>
0xd0bf3828 0 0 null live sun/misc/[email protected]
0xd0d2f370 1 904 null dead sun/reflect/[email protected]
0xd0c99280 1 1440 null dead sun/reflect/[email protected]
0xd0b71d90 0 0 0xd0b5b9c0 live java/util/[email protected]
0xd0d2f4c0 1 904 null dead sun/reflect/[email protected]
0xd0b5bf98 1 920 0xd0b5bf38 dead sun/reflect/[email protected]
0xd0c99248 1 904 null dead sun/reflect/[email protected]
0xd0d2f488 1 904 null dead sun/reflect/[email protected]
0xd0b5bf38 6 11832 0xd0b5b9c0 dead sun/reflect/misc/[email protected]
0xd0d2f338 1 904 null dead sun/reflect/[email protected]
0xd0d2f418 1 904 null dead sun/reflect/[email protected]
0xd0d2f3a8 1 904 null dead sun/reflect/[email protected]
0xd0b5b9c0 317 1397448 0xd0bf3828 live sun/misc/[email protected]
0xd0d2f300 1 904 null dead sun/reflect/[email protected]
0xd0d2f3e0 1 904 null dead sun/reflect/[email protected]
0xd0ec3968 1 1440 null dead sun/reflect/[email protected]
0xd0e0a248 1 904 null dead sun/reflect/[email protected]
0xd0c99210 1 904 null dead sun/reflect/[email protected]
0xd0d2f450 1 904 null dead sun/reflect/[email protected]
0xd0d2f4f8 1 904 null dead sun/reflect/[email protected]
0xd0e0a280 1 904 null dead sun/reflect/[email protected]
total = 22 2186 6746816 N/A alive=4, dead=18 N/A
Начиная с Java 8, команда jmap -clstats <pid> может выводить аналогичную информацию о загрузчике классов и его жизнеспособности, но она показывает количество и размер загруженных классов в Metaspace, а не PermGen.
jmap -clstats 26240
Attaching to process ID 26240, please wait...
Debugger attached successfully. Server compiler detected. JVM version is 25.66-b00 finding class loader instances ..done. computing per loader stat ..done. please wait.. computing liveness.liveness analysis may be inaccurate ...
class_loader classes bytes parent_loader alive? type
<bootstrap> 513 950353 null live <internal>
0x0000000084e066d0 8 24416 0x0000000084e06740 live sun/misc/[email protected]
0x0000000084e06740 0 0 null live sun/misc/[email protected]
0x0000000084ea18f0 0 0 0x0000000084e066d0 dead java/util/[email protected]
total = 4 521 974769 N/A alive=3, dead=1 N/A
Дамп кучи
Как мы упоминали в предыдущих главах, Eclipse MAT, jhat, Java VisualVM, подключаемый модуль JOverflow JMC и Yourkit могут анализировать файлы дампа кучи для анализа и устранения ошибок OutOfMemoryError. Дамп кучи также полезен при решении проблем с памятью в PermGen и Metaspace. Eclipse MAT предоставляет очень хорошую функцию под названием «Дубликаты классов», которая может перечислять классы, которые были загружены несколько раз разными экземплярами загрузки классов. Ограниченное количество повторяющихся классов, загружаемых разными загрузчиками классов, может быть частью дизайна приложения, но если их количество продолжает расти с течением времени, то это опасный признак и требует изучения. Когда на сервере приложений размещается несколько приложений, они работают на одной JVM. Если вы несколько раз удалите и повторно развернете приложения, вы часто столкнетесь с такой ситуацией. Если выгруженное приложение не освобождает все ссылки на созданный им загрузчик классов, JVM не может выгрузить классы, загруженные этим загрузчиком классов, и вновь развернутое приложение перезагрузит эти классы с новым экземпляром загрузчика классов.
Этот снимок показывает, что JaxbClassLoader загрузил повторяющиеся копии класса, потому что приложение неправильно создает новый экземпляр JAXBContext при связывании классов Java для каждого XML.
jcmd
jcmd <pid / classname> GC.class_stats может предоставить более подробную информацию о загруженных классах, с его помощью мы можем увидеть пространство, занимаемое каждым классом Metaspace, как показано в следующем примере выходных данных.
jcmd 2752 GC.class_stats 2752:
Index Super InstBytes KlassBytes annotations CpAll MethodCount Bytecodes MethodAll ROAll RWAll Total ClassName
1 357 821632 536 0 352 2 13 616 184 1448 1632 java.lang.ref.WeakReference
2 -1 295272 480 0 0 0 0 0 24 584 608 [Ljava.lang.Object;
3 -1 214552 480 0 0 0 0 0 24 584 608 [C
4 -1 120400 480 0 0 0 0 0 24 584 608 [B
5 35 78912 624 0 8712 94 4623 26032 12136 24312 36448 java.lang.String
6 35 67112 648 0 19384 130 4973 25536 16552 30792 47344 java.lang.Class
7 9 24680 560 0 384 1 10 496 232 1432 1664 java.util.LinkedHashMap$Entry
8 -1 13216 480 0 0 0 0 0 48 584 632 [Ljava.lang.String;
9 35 12032 560 0 1296 7 149 1520 880 2808 3688 java.util.HashMap$Node
10 -1 8416 480 0 0 0 0 0 32 584 616 [Ljava.util.HashMap$Node;
11 -1 6512 480 0 0 0 0 0 24 584 608 [I
12 358 5688 720 0 5816 44 1696 8808 5920 10136 16056 java.lang.reflect.Field
13 319 4096 568 0 4464 55 3260 11496 7696 9664 17360 java.lang.Integer
14 357 3840 536 0 584 3 56 496 344 1448 1792 java.lang.ref.SoftReference
15 35 3840 584 0 1424 8 240 1432 1048 2712 3760 java.util.Hashtable$Entry
16 35 2632 736 368 8512 74 2015 13512 8784 15592 24376 java.lang.Thread
17 35 2496 504 0 9016 42 2766 9392 6984 12736 19720 java.net.URL
18 35 2368 568 0 1344 8 223 1408 1024 2616 3640 java.util.concurrent.ConcurrentHashMap$Node
…<snip>…
577 35 0 544 0 1736 3 136 616 640 2504 3144 sun.util.locale.provider.SPILocaleProviderAdapter$1
578 35 0 496 0 2736 8 482 1688 1328 3848 5176 sun.util.locale.provider.TimeZoneNameUtility
579 35 0 528 0 776 3 35 472 424 1608 2032 sun.util.resources.LocaleData$1
580 442 0 608 0 1704 10 290 1808 1176 3176 4352 sun.util.resources.OpenListResourceBundle
581 580 0 608 0 760 5 70 792 464 1848 2312 sun.util.resources.TimeZoneNamesBundle
1724488 357208 1536 1117792 7754 311937 1527952 1014880 2181776 3196656 Total
53.9% 11.2% 0.0% 35.0% - 9.8% 47.8% 31.7% 68.3% 100.0%
Index Super InstBytes KlassBytes annotations CpAll MethodCount Bytecodes MethodAll ROAll RWAll Total ClassName
Из этого вывода мы можем увидеть имя загруженного класса (ClassName) байты, занимаемые каждым классом (KlassBytes), байты, занятые экземплярами каждого класса (InstBytes), количество методов в каждом классе (MethodCount), пространство, занимаемое байт-кодом (ByteCodes)) и многое другое.
Следует отметить, что в Java 8 эта диагностическая команда требует, чтобы процесс Java запускался с параметром -XX: + UnlockDiagnosticVMOptions.
jcmd 33984 GC.class_stats 33984:
GC.class_stats command requires -XX:+UnlockDiagnosticVMOptions
В Java 9 диагностическая команда не требует -XX: + UnlockDiagnosticVMOption.
OutOfMemoryError: Native Memory
Вот некоторые примеры OutOfMemoryError в собственной памяти:
OutOfMemoryError из-за нехватки места для подкачки:
# A fatal error has been detected by the Java Runtime Environment:
#
# java.lang.OutOfMemoryError: requested 32756 bytes for ChunkPool::allocate. Out of swap space?
#
# Internal Error (allocation.cpp:166), pid=2290, tid=27 # Error: ChunkPool::allocate
Ошибка OutOfMemoryError из-за нехватки памяти процесса:
# A fatal error has been detected by the Java Runtime Environment:
#
# java.lang.OutOfMemoryError : unable to create new native Thread
Эти ошибки ясно указывают на то, что JVM не может выделить собственную память. Это может быть связано с тем, что сам процесс потребляет всю собственную память или другие процессы в системе используют внутреннюю память. После использования pmap (или других инструментов сопоставления собственной памяти) для отслеживания использования собственной кучи мы можем соответствующим образом настроить кучу Java, количество потоков и размер стека, чтобы обеспечить достаточно места для собственной кучи. Если мы найдем собственную кучу Использование постоянно увеличивается, и в конечном итоге появится OutOfMemoryError, что может указывать на то, что мы столкнулись с утечкой собственной памяти.
Собственная куча OutOfMemoryError на 64-битной JVM
В 32-разрядной JVM верхний предел размера процесса составляет 4 ГБ, поэтому в 32-разрядном процессе Java легче исчерпать внутреннюю память. Однако в 64-битной JVM нет ограничений на использование памяти. С технической точки зрения мы можем подумать, что никогда не столкнемся с исчерпанием собственной кучи, но это не так. Собственная куча обнаруживает OutOfMemoryErrors. Не редкость. Это связано с тем, что 64-разрядная JVM по умолчанию включает функцию CompressedOops, и реализация этой функции будет определять, где разместить Java в адресном пространстве. Расположение кучи Java может ограничивать максимальную емкость собственной памяти. На карте памяти ниже куча Java выделена на границе адреса 8 ГБ, оставляя около 4 ГБ для собственной кучи. Если приложению необходимо выделить много места в собственной памяти, превышающее 4 ГБ, даже если в системе все еще имеется много доступной памяти, оно все равно выдаст ошибку OutOfMemoryError в собственной куче.
0000000100000000 8K r-x-- /sw/.es-base/sparc/pkg/jdk-1.7.0_60/bin/sparcv9/java
0000000100100000 8K rwx-- /sw/.es-base/sparc/pkg/jdk-1.7.0_60/bin/sparcv9/java
0000000100102000 56K rwx-- [ heap ]
0000000100110000 2624K rwx-- [ heap ] <--- native Heap
00000001FB000000 24576K rw--- [ anon ] <--- Java Heap starts here
0000000200000000 1396736K rw--- [ anon ]
0000000600000000 700416K rw--- [ anon ]
Эту проблему можно решить с помощью параметра -XX: HeapBaseMinAddress = n, который может указать начальный адрес кучи Java. Установка более высокого адреса оставит больше места для собственной кучи.
Для получения информации о том, как диагностировать, устранять неполадки и решать эту проблему, см.СтатьяУзнайте больше об этом.
Родная куча: средство диагностики
Давайте посмотрим на инструменты обнаружения утечек памяти, они могут помочь нам найти причину утечек собственной памяти.
Отслеживание собственной памяти
JVM имеет мощную функцию под названием Native Memory Tracking (NMT), которая используется внутри JVM для отслеживания собственной памяти. Следует отметить, что он не может отслеживать память, выделенную вне JVM или собственных библиотек. С помощью следующих двух простых шагов мы можем отслеживать использование собственной памяти JVM:
- Запустите процесс, включив NMT. Уровень вывода может быть установлен на уровень «сводка» или «детализация»:
- -XX:NativeMemoryTracking=summary
- -XX:NativeMemoryTracking=detail
- Используйте jcmd, чтобы получить подробную информацию об использовании собственной памяти:
- jcmd <pid> VM.native_memory
Пример вывода NMT:
d:tests>jcmd 90172 VM.native_memory 90172:
Native Memory Tracking:
Total: reserved=3431296KB, committed=2132244KB
- Java Heap (reserved=2017280KB, committed=2017280KB)
(mmap: reserved=2017280KB, committed=2017280KB)
- Class (reserved=1062088KB, committed=10184KB)
(classes #411)
(malloc=5320KB #190)
(mmap: reserved=1056768KB, committed=4864KB)
- Thread (reserved=15423KB, committed=15423KB)
(thread #16)
(stack: reserved=15360KB, committed=15360KB)
(malloc=45KB #81)
(arena=18KB #30)
- Code (reserved=249658KB, committed=2594KB)
(malloc=58KB #348)
(mmap: reserved=249600KB, committed=2536KB)
- GC (reserved=79628KB, committed=79544KB)
(malloc=5772KB #118)
(mmap: reserved=73856KB, committed=73772KB)
- Compiler (reserved=138KB, committed=138KB)
(malloc=8KB #41)
(arena=131KB #3)
- Internal (reserved=5380KB, committed=5380KB)
(malloc=5316KB #1357)
(mmap: reserved=64KB, committed=64KB)
- Symbol (reserved=1367KB, committed=1367KB)
(malloc=911KB #112)
(arena=456KB #1)
- Native Memory Tracking (reserved=118KB, committed=118KB)
(malloc=66KB #1040)
(tracking overhead=52KB)
- Arena Chunk (reserved=217KB, committed=217KB)
(malloc=217KB)
Подробнее об использовании команды jcmd для доступа к данным NMT и чтении ее вывода см.Статья。
Средство обнаружения утечек собственной памяти
В случае утечек собственной памяти за пределами JVM нам нужно полагаться на инструменты утечки собственной памяти для обнаружения и разрешения. Нативные инструменты могут помочь нам решить проблему утечек собственной памяти за пределами JVM. Такие инструменты включаютdbx、libumem、valgrindтак же какpurifyПодождите.
подводить итоги
Устранение проблем с памятью может быть очень трудным и запутанным, но правильные методы и соответствующие инструменты могут значительно упростить этот процесс. Мы видели, что Java HotSpot JVM будет сообщать о различных сообщениях OutOfMemoryError. Очень важно четко понимать эти сообщения об ошибках. В наборе инструментов есть различные инструменты для диагностики и устранения неполадок, которые помогают нам диагностировать и устранять эти проблемы.
Подлинный китайский: http://www.infoq.com/cn/articles/Troubleshooting-Java-Memory-Issues#
Оригинал на английском языке: https://www.infoq.com/articles/Troubleshooting-Java-Memory-Issues
An OutOfMemoryError
is an exception thrown by the Java Virtual Machine (JVM) because it needs to allocate memory for a (new) object, but insufficient memory is available for the object. The JVM will have first tried to free memory used by dead objects, by running the garbage collector.
As an OutOfMemoryError
is a VirtualMachineError
, the JVM is permitted to throw it at any time, although it must try to release memory through garbage collection first.
However, in practice it is likely to be thrown from a new
statement that tried to create an object for which memory could not be allocated. Therefore, you should first examine the stacktrace associated with the exception for clues about the cause of the problem, as you would for any other exception.
- If the exception is thrown from an attempt to allocate an array (such as
int[] values = new int[n]
), the cause could be that you are trying to create an excessively large array (n
is too large). Have you made a mistake in the calculation of the size of array you need? - If the exception is thrown from an attempt to allocate an array in a method of a container class written by someone else, the cause could be that your code is asking the container to store an excessive number of things. Methods such as
ArrayList.reserve(int)
andHashMap(int)
must allocate storage for future use. Have you made a mistake in the calculation of the size of container you need? - If the exception is thrown from inside a loop, the cause could be that the code has looped too many times. Is your loop termination condition correct? If it is a
for
loop, are you asking it to loop the correct number of times?
If the stacktrace does not provide enough clues, you could try using a heap profiler. That is a monitoring program that enables you to examine the memory used for objects while the program runs, or examies a heap dump written when the program exits. It can provide information about the sizes, number and classes of objects stored in memory.
The JVM has a finite amount of memory made available to it. You might conclude that you program is behaving correctly, but just needs more memory to run than has been made available to it. If you do not explicitly tell the JVM how much memory to use, most implementations will choose a sensible default amount based on the amount of RAM that your computer has, but that amount could be too small for your program. Command line options for the JVM can control how much memory is made available. For most JVM implementations, the most important of those options are -Xmx
and -Xms
.
Тема данного поста — довольно часто встречающаяся проблема переполнения памяти в Java. Постараюсь рассказать,
почему этой ошибке стоит уделить особое внимание, почему так сложно искать причину и какие утилиты в этом помогают.
Ошибка OutOfMemoryError в java неизбежна. Если в нагруженном приложении такая ошибка не возникает, значит она была
раньше и её починили. Я немного утрирую, но я не помню проекта, на котором бы такая ошибка рано или поздно не проявлялась.
На собеседованиях вопрос про OutOfMemory не очень часто задают; честно говоря, редко кто отвечает правильно и
подробно.
Даже несмотря на то, что такая ошибка может приводить к реальным убыткам, так как чаще всего возникает под нагрузкой,
на production instance (продакшне, по-народному) и приводит к параличу системы на
часы и более, всё равно в 90% случаев вместо поиска причины ошибки люди увеличивают объём памяти JVM (хоть до -Xmx64g) и
настраивают еженедельные, а потом и ежедневные рестарты сервера.
Чтобы система не повисала, настраивают мониторинги и когда память приближается к пороговому значению, проводят
небольшой анализ, который, как правило, ничего не даёт, а уже потом перезагружают сервер.
Но что делать если рестарт или увеличение памяти не помогают?
Тогда вся команда бросит свои текущие задачи и будет искать причину, воспроизводить, изучать логи, дампы памяти и
потоков. Тогда, конечно, причину найдут и починят. Конечно, не всегда всё так серьезно, но бывает и так.
Как вообще происходит управление памятью в Java? Тот, кто знаком с языками С или C++,
знает каково это явно выделять память под каждый массив или объект и потом так же явно освобождать её.
Пример работы с массивом в C++:
int * p;
p = new (nothrow) int[1000]; //выделяем память под массив
if (p == nullptr) //обрабатываем ситуацию, если память выделить не удалось
cout << "Error: memory could not be allocated";
delete[] p; //освобождаем память после использования
Пример создания массива в Java:
int[] array = new int[1000];
В С++ легко было определить термин «утечка памяти» — память была выделена, но не была освобождена, при этом в программе она больше никак использоваться не будет.
Для этого достаточно «потерять» указатель («int * p» в нашем примере, например, после выхода из функции, если он был локальной переменной этой функции.)
Когда память заканчивается, это событие можно сразу же явно обработать.
В Java же мы не управляем ни выделением памяти, ни его освобождением, ни моментом, когда память заканчивается. Попытка перехватить и обработать
исключение OutOfMemoryError бессмысленна — после её возникновения дальнейшее поведение программы не определено.
Вроде как память освобождает специальный сборщик мусора, но им тоже никак нельзя управлять.
Его можно вежливо попросить освободить ненужную занятую память с помощью команды System.gc(), но этот вызов ничего не гарантирует.
Под «утечкой памяти» применительно к Java подразумевают то, что объект, под который она выделена,
никогда больше не будет использоваться в соответствии с потоком выполнения и логикой программы, но память, занимаемая им не может быть освобождена сборщиком мусора.
Можно выделить две основных причины OutOfMemoryError: первая — программе действительно нужно больше памяти, вторая — в программе присутствует утечка.
Первый случай решается либо переработкой кода с целью эффективнее расходовать память, либо простым увеличением объёма памяти выделенной под процесс.
Во втором же случае что бы мы не делали, память рано или поздно закончится, если причина утечки не будет исправлена.
Почему же помогают рестарты сервера? Дело в том, что утечка расходует память не мгновенно. Например, после каждого вызова сервиса теряется один мегабайт.
Если за сутки сервис вызывается 1000 раз, а для процесса выделено 2Гб, то ежедневные рестарты спасут от OutOfMemory, пока дневная нагрузка не поднимется сильно выше средней.
Я не привожу строгого формального описания процесса управления памятью в Java и работы сборщика мусора.
Я считаю, что на практике в первую очередь нужно разобраться с основным принципом.
Выделением и освобождением памяти занимается JVM.
Так или иначе если объект становится неиспользуемым (недостижимым по графу ссылок), сборщик мусора его освободит до того, как память полностью закончится.
Если объект всё ещё достижим, то память, занимаемая этим объектом, не будет освобождена вне зависимости от настроек сборщика мусора и конкретной его реализации.
Появление достижимых объектов, которые более никогда не будут использоваться в программе — это утечка памяти, которую следует исправлять.
Достижимость — это существование пути в графе ссылок от корневых объектов (GC Roots) до целевого объекта.
Вот основные из них:
- Классы, их статические поля
- Все запущенные потоки
- Стек вызовов
- Локальные переменные и параметры функций
Упрощённо достижимость можно объяснить так: если в программе есть способ обратиться к объекту, значит он достижим (из GC Roots).
Мы можем обратиться к статическим полям загруженных классов, к локальным переменным и параметрам, к текущему потоку.
Утечек памяти, связанных с тонкостями определения корневых объектов, я не встречал. Возможно, они имеют место в случае использования
собственных загрузчиков классов (custom classloaders). В такие дебри лезть не будем.
Рассмотрим несколько примеров. В первом создаём бесконечный список, OutOfMemoryError вылетает мгновенно.
IntStream.generate(() -> 1).boxed().collect(Collectors.toList());
//Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
// at java.base/java.util.Arrays.copyOf(Arrays.java:3689)
// at java.base/java.util.ArrayList.grow(ArrayList.java:237)
Во втором бесконечно вызываем String.intern, сохраняющий строку во внутреннем пуле.
Потребление оперативной памяти не растёт со временем, ошибка переполнения не происходит.
for(;;) {
UUID.randomUUID().toString().intern();
}
Начиная с версии java 1.8 PermGen space был удалён и данный код стал работать немного иначе: раньше все строки,
добавленные во внутренний пул, хранились там вечно, и злоупотребление методом intern вместо экономии памяти могло наоборот привести к её переполнению.
Теперь же возможно не задумываясь вставлять вызовы .intern() хоть для каждой переменной типа String. Делать этого, конечно же, не нужно.
Вообще не так просто придумать приложение, в котором при разумном проектировании в памяти одновременно находится очень много одинаковых строковых значений.
Теперь рассмотрим неочевидный случай, анализ которого потребует специальных утилит.
Код тут., хотя в нём буквально 10 строчек:
@RestController
@RequestMapping("/api")
public class OomRestService {
private final Logger log = LoggerFactory.getLogger(OomRestService.class);
@GetMapping("/ping")
public String ping(HttpServletRequest request) {
log.trace("req from {}, session id {}", request.getRemoteHost(), request.getSession().getId());
return "pong";
}
}
Тривиальный сервис, который на HTTP GET-запрос по адресу «/api/ping» отвечает «pong».
Вызывать я его буду в бесконечном цикле из теста:
@Test
public void testPingInfiniteOom() {
for(;;) {
ResponseEntity<String> ping =
testRestTemplate.getForEntity("http://localhost:" + port + "/api/ping", String.class);
}
}
Кстати, в Junit5 появилась очень удобная аннотация @RepeatedTest, но она замедляет исполнение,
поэтому вместо неё здесь использую некрасивый бесконечный цикл.
Тест запускается с дополнительным аргументом виртуальной машины «-Xmx50m»,
то есть всего 50 мегабайт, но для старта приложению достаточно и 20 мегабайт, а OutOfMemory мы получим и при гигабайте, только ждать дольше.
Ошибка случается через пару минут и около 80 000 вызовов сервиса.
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "http-nio-auto-1-ClientPoller"
Exception in thread "RMI TCP Connection(idle)" java.lang.OutOfMemoryError: Java heap space
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
Как обычно бывает в таких случаях, никакой информации о том, что привело к переполнению памяти, нет.
Иногда стректрейс указывает упавшую операцию, как в первом примере. Но это не означает, что именно она является причиной утечки,
так как когда памяти остаётся совсем мало, любая рядовая операция может завершиться с ошибкой, при этом вся память забита совершено другими объектами.
В данном случае у нас нет стектрейса и придётся проводить полноценное расследование.
Первое, что нужно получить — дамп памяти процесса JVM перед падением. Для этого тест следует запустить с дополнительным параметром:
-XX:+HeapDumpOnOutOfMemoryError
Перед тем, как перезапускать тест, установим две утилиты, которые помогут нам в анализе:
Первая — Visual VM. До Java 9 эта утилита входила в состав Oracle JDK.
В более поздних версиях устанавливается отдельно. Позволяет подключиться к процессу с помощью технологии JMX и осуществлять разнообразную диагностику и даже управление.
Например, можно изучать состояние потоков, загрузку процессора и памяти.
Для данного примера я установил плагин VisualGC (Tools->Plugins). С его помощью мы в динамике сможем понаблюдать
за заполнением памяти и состоянием программы в момент возникновения OutOfMemory.
Если процесс запущен локально, то подключение не вызывает проблем, никаких портов специально открывать не нужно.
Вторая утилита — Eclipse Memory Analyzer Tool.
С её помощью мы будем анализировать дамп памяти, чтобы определить вероятные причины проблемы.
Итак, запускаем тест с параметрами «-Xmx50m -XX:+HeapDumpOnOutOfMemoryError», после чего сразу запускаем VisualVM.
В левой панели видим список всех Java процессов, запущенных на локальной машине. Среди них будет IDE, VisualVM и прочие.
Нам нужен com.intellij.rt.JUnitStarter, он добавится в список через пять-десять секунд, выбираем его и подключаемся двойным щелчком левой кнопки мыши.
Теперь открываем вкладку Monitor или VisualGC и ждём, пока программа не упадёт. Очень познавательно наблюдать за работой сборщика мусора вот так наглядно.
Видно, как меняются графики, когда памяти остаётся мало. Таким образом можно исследовать приложение: совершать пользовательские операции
и сразу же визуально оценивать их влияние на потребление ресурсов. В нашем случае никаких операций совершать не нужно, просто ждём две-три минуты.
Процесс завершается с ошибкой, а в корне проекта создаётся файл .hprof (в моем случае, java_pid11712.hprof, 85мб),
размер которого сопоставим с объёмом выделенной памяти. Вот что получилось у меня:
Следующий шаг — запустить Eclipse MAT, открыть дамп памяти и произвести поиск утечек (File->Open Heap Dump->Leak Suspect).
Логика определения утечек следующая — если в нормальном состоянии работы приложения занято не больше 50-60 процентов от всей выделенной памяти,
то переполнение происходит от того, что какой-то объект или однотипные объекты, накапливающиеся в результате утечки памяти, занимают всё оставшееся место.
По дампу памяти несложно определить что, скажем, вся память забита объектами типа String, но это не очень полезно.
Eclipse MAT же строит дерево на основе ссылок и показывает подозрительные узлы, суммируя память по всем поддеревьям.
Результат анализа дампа нашего теста:
Видно, что 71% занятой памяти как-то связан с классом org.apache.catalina.session.StandardManager.
Это-ещё не ответ, а лишь подсказка и предположение, которое следует проверить. Но оно очень ценно, без него мы были практически в тупике.
Следующие шаги поиска проблемы будут напрямую зависеть от того, чем занята память, здесь нет общего рецепта.
Может быть, это будет какой-то кэш, который не очищается,
может нагрузка слишком велика и столько одновременных запросов система не способна обработать.
Конечно, в данном случае я точно знал причину заранее. Она следует из принципов работы технологии сервлетов.
На каждый новый запрос, поступающий к приложению, создаётся объект HttpSession, а пользователю в ответе добавляется заголовок, устанавливающий куку
JSESSIONID. За счёт этого работает аутентификация и всевозможное кэширование.
Это особенно характерно для корпоративных приложений, в которых обычно небольшое количество пользователей,
но приличный объём данных и очень сложная бизнес логика. Главная страница открывается долго, потому что загружаются и обрабатываются сложные структуры данных, права, настройки текущего пользователя.
Всё это сохраняется в сессии (наподобие session.setAttribute(«settings», settings)).
Потребовалось 80 000 пустых сессий, чтобы заполнить 50мб памяти. При использовании фреймворка Vaadin одна сессия по слухам требует уже 0.1мб,
ну а в реальном приложении можно и всю память забить данными, сохранёнными в атрибутах одной сессии.
Важно здесь то, что сессия создаётся на каждый запрос и остаётся активной до истечения таймаута, который по умолчанию составляет 30 минут.
Если установить малое значение, то сессии начнут становиться неактивными быстрее, чем переполнится память.
Проверим это предположение и установим таймаут в одну минуту:
server:
servlet:
session:
timeout: 1
В документации сказано, что значение в секундах, но Tomcat в текущей реализации умножает на 60, поэтому 1 — одна минута.
Время в секундах можно выставить прямо на объекте сессии методом setMaxInactiveInterval.
Перезапускаем тест, память больше не переполняется. Мне ещё потребовалось увеличить память до 150мб (-Xmx150m),
потому что иначе ошибка выпадала быстрее, чем за минуту.
Наглядно:
Сессия не создавалась бы вовсе, если бы не вот эта строчка кода, которая имитирует неаккуратное логирование, приводящее к побочным эффектам:
log.trace("req from {}, session id {}", request.getRemoteHost(), request.getSession().getId());
Если её удалить или использовать дополнительный параметр при вызове getSession (request.getSession(false)), то сессия не будет создаваться вообще.
Такое поведение — скорее исключение, потому что как минимум аутентификации хранится в сессии, а уж аутентификация есть практически в любом корпоративном приложении.
Чтобы сервисы не имели состояния (stateless), нужно заранее проектировать систему таким образом, использовать явные настройки и тестировать их.
Например, в Spring Security есть разные конфигурации создания сессии: always, ifRequired, never, stateless. Это отдельная тема.
Здесь же я хотел показать общую схему поиска ошибки:
- Воспроизведение OutOfMemoryError
- Получение дампа памяти
- Диагностика приложения в динамике
- Анализ дампа памяти, гипотеза
- Проверка гипотезы
- Hotfix: Быстрое решение, чтобы починить приложение прямо сейчас
- Long term solution: Правильное решение, исправление кода или даже изменение дизайна.
У нас быстрое решение — уменьшить таймаут сессии и, по возможности дать больше памяти процессу. Правильное решение — перейти
на stateless сервис, удалить ошибочное создание сессии без необходимости либо уменьшить размер данных, хранящихся в сессии,
перейдя на специализированные библиотеки кэширования.
Если Вы сами повторяли шаги анализа, то могли заметить, насколько приложение замедляется перед тем, как случается ошибка OutOfMemoryError.
Плата за удобство управления памятью — время, которое требуется сборщику мусора на каждый цикл его работы.
Реализация сборщика мусора G1, которая используется по умолчанию, периодически полностью останавливает выполнение команд.
Это называется Stop-the-world. Другие стандартные реализации поступают так же: Serial, Parallel, CMS.
В обычном состоянии эти паузы малы и практически не влияют на производительность, хотя и не позволяют использовать Java для «real time» приложений.
Но когда существует утечка и свободной памяти остаётся всё меньше, работа сборщика мусора превращается в сизифов труд.
Такое состояние ещё хуже, чем если бы ошибка выпадала сразу.
Иногда JVM удаётся определить подобное состояние и выбрасывается исключение:
java.lang.OutOfMemoryError: GC overhead limit exceeded
Чаще всего это означает утечку памяти, заполняющую её не очень быстро.
Отдельной темой будет анализ логов сборщика мусора, поиск там событий «Full GC» и так далее.
Приведу лишь ссылку на статью на эту тему.
По моему опыту, эффективный анализ производится по дампам памяти, а логи GC используются, чтобы исключить из рассмотрения
проблемы утечек памяти при анализе проблем с производительностью приложения. Как правило, при достижении верхней границы памяти события
Full GC начинают происходить одно за другим.
В заключении приведу несколько примеров реальных и выдуманных ситуаций, приводящих к переполнению памяти и варианты решения.
Кэширование
Если применяется библиотека кэширования, то следует ограничивать не только максимальное время жизни объектов и их количество,
но и максимальный объём памяти. Тогда переполнения точно не случится, в худшем случае мы получим слишком быстрое удаление объектов из кэша.
Пример конфигурации для Hazelcast:
<max-size policy="USED_HEAP_SIZE">4096</max-size>
А вот так красиво можно настраивать Apache Geode:
<gfe:partitioned-region-template id="PartitionRegionTemplate" template="ExtendedRegionTemplate"
copies="1" load-factor="0.70" local-max-memory="1024"
total-max-memory="16384" value-constraint="java.lang.Object">
С другой стороны, библиотека JCS, например, не позволяет явно указывать максимальный объём памяти (по крайней мере, я в своё время не нашёл такой конфигурации).
В общем, идея в том, что ограничивать количество объектов имеет смысл, если их размер стабилен, но и тогда придётся считать и исследовать.
А ограничив общее потребление памяти, легко подобрать конфигурацию так, чтобы суммарно весь кэш занимал предсказуемый объём памяти и не приводил к переполнению.
Неожиданно большие коллекции в базе данных
При использовании в JPA отношений OneToMany или ManyToMany, когда загрузка одного объекта из базы влечет за собой загрузку списка связанных объектов,
существует два варианта загрузки: ленивый и жадный (Lazy, Eager). При разумном подходе все коллекции либо, по крайней мере, большую часть помечают как Lazy.
Это означает, что они не будут инициализироваться без необходимости.
Но для удобства часть коллекций можно оставить как Eager. Подобные коллекции могут привести к переполнению памяти,
если их размер со временем разрастется в связи со спецификой нагрузки на продакшне.
Другой случай — коллекция действительно нужна в конкретном сценарии использования.
Скажем, мы показываем администратору неудачные попытки ввода пароля за сутки на одной странице.
Обычно их 10 — 100. Код может работать отлично, пока это предположение не нарушено, но в один прекрасный день в результате какой-нибудь DDOS атаки или просто ошибки в коде,
коллекция вырастает до 500 000 элементов, и приложение закономерно падает с OutOfMemoryError.
На практике лучше избегать предположений по размеру коллекций.
Возможно, для этого придётся поменять бизнес требования, реализовывать постраничное отображение информации или ограничения (constraints) на уровне базы данных.
Неожиданно большие файлы
Если входные данные от сторонних систем поступают в виде файлов, нужно избегать полной загрузки файла в память.
Скорее даже не самого файла, а объектной модели, соответствующей его содержимому.
Для большинства форматов данных (например, JSON, XML, CSV) существуют способы потоковой обработки информации
без отказа от удобной объектной модели.
Утечки в сторонних библиотеках
Они — возможны, но намного реже утечек в коде приложения. Такие проблемы исследовать очень тяжело.
Если на основе анализа в Eclipse MAT появляется гипотеза о некорректном поведении библиотеки, то чтобы её проверить может потребоваться разработка
синтетического приложения, которое отражает только один сценарий работы приложения, связанный с этой библиотекой, а всё остальное из него исключено.
Впрочем, этот подход справедлив для исследования любых подозрений на ошибки в сторонних библиотеках, не только об утечках памяти.
Заключение
Причины ошибки переполнения памяти слишком разнообразны, чтобы можно было предложить универсальное решение.
Однако разумный подход и знание типовых ошибок во многом спасает.
Данная публикация посвящается всем ценителям Minecraft и других игр, требующих установки Java. На днях знакомые попросили помочь: при попытке запустить Майнкрафт у них появлялось сообщение — Java Virtual Machine Launcher ошибка, как исправить сейчас расскажу.
Не хватает памяти
При установке Джавы, необходимой для работы некоторых игр, отображалось следующее окно:
Она указывает на то, что для создаваемой виртуальной машины не хватает памяти. Очень часто подобная ситуация возникает, если некорректно выходить из игры (нажав Alt + F4 ) или при внезапном отключении ПК.
В моем случае, ошибка появилась после того, как пользователь случайно удалил компонент Java, потом пытался его переустановить, но на последнем этапе инсталляции отображалось вышеупомянутое окно.
На зарубежных источниках нашел два решения. Начну с более простого.
Настройка переменной среды
- Открываем панель управления. В Windows 7 это можно сделать через меню «Пуск», в «десятке» достаточно кликнуть правой кнопкой мышки по кнопке «Старт» (или нажать Win + X ), и выбрать из списка нужный элемент:
- Переходим к разделу «Система»:
- Слева кликаем по ссылке «Дополнительные параметры…»:
- В новом окне снизу есть кнопка «Переменные среды», которую стоит нажать:
- Кликаем по кнопке «Создать…», присваиваем новой переменной:
имя «_JAVA_OPTIONS»
значение «-Xmx512M»
- Обязательно сохраняем все изменения, нажав на ОК. Перезагрузка компьютера не требуется.
Теперь при запуске установщика Джава ошибка Java Virtual Machine Launcher не появиться.
Переходим ко второму способу.
Создание файла запуска
Покажу всё на примере Minecraft.
- Открываем пользовательскую папку, где хранятся игровые настройки. Для этого нажимаем Win + R и пишем команду:
%appdata%
- На экране отобразится каталог, в котором нужно найти игровую директорию, войти в неё и внутри создать новый текстовый документ (Блокнот):
- Туда стоит записать следующую фразу:
java -Xms650m -jar "c:UsersUSER_NAMEAppDataRoaming.minecraftminecraft.exe
Вместо USER_NAME пишем название своего аккаунта. Оно указано в адресной строке окна. Учетная запись не должна содержать русских символов. Только латинские (английские), иначе способ не сработает.
- Теперь нажимаем на пункт меню «Файл», затем выбираем «Сохранить как». Указываем имя «minecraft.bat» и указываем тип «Все…», как показано на скриншоте:
- Осталось только создать ярлык для созданного элемента и отправить его на рабочий стол, чтобы через него запускать игру.
Видео
Вы узнали, как исправить ошибку Java Virtual Machine Launcher. Но если вопросы остались, обязательно задавайте их с помощью формы комментирования внизу страницы.
Рекомендуем:
- Устраняем ошибку «Опаньки» в Google Chrome
- Что делать если не работает безопасный режим в Windows 10
- Как восстановить удаленные фотографии, сообщения, контакты на iPhone?
- d3dx9_43.dll скачать, чтобы устранить ошибку на компьютере
- Четыре способа получить роль администратора в Windows 10
Вам помогло? Поделитесь с друзьями — помогите и нам!
Время на прочтение
17 мин
Количество просмотров 71K
Приветствую, Хабр!
Немного лирики
Сегодня, 2015-03-21, я решил сделать пол-дела, и всё-таки начать писать статью о том, как же всё-таки начать понимать, что же делать с OOM, да и вообще научиться ковырять heap-dump’ы (буду называть их просто дампами, для простоты речи. Также я постараюсь избегать англицизмов, где это возможно).
Задуманный мной объём «работ» по написанию этой статьи кажется мне не однодневным, а посему статья должна появиться лишь
через пару недель
спустя день.
В этой статье я постараюсь разжевать, что делать с дампами в Java, как понять причину или приблизиться к причине возникновения OOM, посмотреть на инструменты для анализа дампов, инструмент (один, да) для мониторинга хипа, и вообще вникнуть в это дело для общего развития. Исследуются такие инструменты, как JVisualVM (рассмотрю некоторые плагины к нему и OQL Console), Eclipse Memory Analyzing Tool.
Очень много понаписал, но надеюсь, что всё только по делу
Предыстория
Для начала нужно понять, как возникает OOM. Кому-то это может быть ещё неизвестно.
Представьте себе, что есть какой-то верхний предел занимаемой оперативки для приложения. Пусть это будет гигабайт ОЗУ.
Само по себе возникновение OOM в каком-то из потоков ещё не означает, что именно этот поток «выжрал» всю свободную память, да и вообще не означает, что именно тот кусок кода, который привёл к OOM, виноват в этом.
Вполне нормальна ситуация, когда какой-то поток чем-то занимался, поедая память, «дозанимался» этим до состояния «ещё немного, и я лопну», и завершил выполнение, приостановившись. А в это время какой-то другой поток решил запросить для своей маленькой работы ещё немного памяти, сборщик мусора попыжылся, конечно, но мусора уже в памяти не нашёл. В этом случае как раз и возникает OOM, не связанный с источником проблемы, когда стектрейс покажет совсем не того виновника падения приложения.
Есть и другой вариант. Около недели я исследовал, как улучшить жизнь парочки наших приложений, чтобы они перестали себя нестабильно вести. И ещё недельку-две потратил на то, чтобы привести их в порядок. В общей сложности пара недель времени, которые растянулись на полтора месяца, ведь занимался я не только этими проблемами.
Из найденного: сторонняя библиотека, и, конечно же, некоторые неучтённые вещи в вызовах хранимых процедур.
В одном приложении симптомы были следующие: в зависимости от нагрузки на сервис, оно могло упасть через сутки, а могло через двое. Если помониторить состояние памяти, то было видно, что приложение постепенно набирало «размер», и в определённый момент просто ложилось.
С другим приложением несколько интереснее. Оно может вести себя хорошо длительный срок, а могло перестать отвечать минут через 10 после перезагрузки, или вдруг внезапно упасть, сожрав всю свободную память (это я уже сейчас вижу, наблюдая за ним). А после обновления версии, когда была изменена и версия Tomcat с 7й до 8й, и JRE, оно вдруг в одну из пятниц (проработав вменяемо до этого ни много ни мало — 2 недели) начало творить такие вещи, что стыдно признаваться в этом.
В обоих историях очень полезны оказались дампы, благодаря им удалось отыскать все причины падений, подружившись с такими инструментами, как JVisualVM (буду называть его JVVM), Eclipse Memory Analyzing Tool (MAT) и языком OQL (может быть я не умею его правильно готовить в MAT, но мне оказалось легче подружиться с реализацией OQL именно в JVVM).
Ещё вам понадобится свободная оперативка для того, чтобы было куда загружать дампы. Её объём должен быть соизмерим с размером открываемого дампа.
Начало
Итак, начну потихоньку раскрывать карты, и начну именно с JVVM.
Этот инструмент в соединении с jstatd и jmx позволяет удалённо наблюдать за жизнью приложения на сервере: Heap, процессор, PermGen, количество потоков и классов, активность потоков, позволяет проводить профилирование.
Также JVVM расширяем, и я не преминул воспользоваться этой возможностью, установив некоторые плагины, которые позволили куда больше вещей, например, следить и взаимодействать с MBean’ами, наблюдать за деталями хипа, вести длительное наблюдение за приложением, держа в «голове» куда больший период метрик, чем предоставляемый вкладкой Monitor час.
Вот так выглядит набор установленных плагинов.
Visual GC (VGC) позволяет видеть метрики, связанные с хипом.
Детальнее о том, из чего состоит хип в этой нашей Java
Вот два скриншота вкладки VGC, которые показывают, как ведут себя два разных приложения.
Слева Вы можете увидеть такие разделы хипа, как Perm Gen, Old Gen, Survivor 0, Survivor 1, и Eden Space.
Все эти составляющие — участки в оперативке, в которую и складываются объекты.
PermGen — Permanent Generation — область памяти в JVM, предназначенная для хранения описания классов Java и некоторых дополнительных данных.
Old Gen — это область памяти для достаточно старых объектов, которые пережили несколько перекладываний с места на место в Survivor-областях, и в момент какого-то очередного переливания попадают в область «старых» объектов.
Survivor 0 и 1 — это области, в которые попадают объекты, которые после создания объекта в Eden Space пережили его чистку, то есть не стали мусором на момент, когда Eden Space начал чиститься Garbage Collector’ом (GC). При каждом запуске чистки Eden Space объекты из активного в текущий момент Survivor’а перекладываются в пассивный, плюс добавляются новые, и после этого Survivor’ы меняются статусами, пассивный становится активным, а активный — пассивным.
Eden Space — область памяти, в которой новые объекты порождаются. При нехватке памяти в этой области запускается цикл GC.
Каждая из этих областей может быть отрегулирована по размеру в процессе работы приложения самой виртуальной машиной.
Если вы указываете -Xmx в 2 гигабайта, например, то это не означает, что все 2 гигабайта будут сразу же заняты (если не запускать сразу что-то активно кушающее память, конечно же). Виртуальная машина сначала постарается держать себя «в узде».
На третьем скриншоте видно неактивную стадию приложения, которое не используется на выходных — Eden растёт равномерно, Survivor’ы перекладываются через равные промежутки времени, Old практически не растёт. Приложение проработало больше 90 часов, и в принципе JVM считает, что приложению требуется не так уж и много, около 540 МБ.
Бывают пиковые ситуации, когда виртуальная машина даже выделяет под хип гораздо больше памяти, но я думаю, что это какие-то ещё «неучтёнки», о которых я расскажу детальнее ниже по тексту, а может просто виртуальная машина выделила больше памяти под Eden, например, чтобы объекты в нём успевали стать мусором до следующего цикла очистки.
Участки, которые на следующем скриншоте я обозначил красным — это как раз возрастание Old, когда некоторые объекты не успевают стать мусором, чтобы быть удалёнными из памяти ранее, и всё-таки попадают в Old. Синий участок — исключение. На протяжении красных участков можно видеть гребёнку — это Eden так себя ведёт.
На протяжении синего участка скорее всего виртуальная машина решила, что нужно увеличить размер Eden-области, потому как при увеличении масштаба в Tracer’е видно, что GC перестал «частить» и таких мелких колебаний, как ранее, теперь нет, колебания стали медленными и редкими.
Перейдём ко второму приложению:
В нём Eden напоминает мне какой-то уровень из Mortal Kombat, арену с шипами. Была такая, кажется… А График GC — шипы из NFS Hot Pursuit, вот те вот, плоские ещё.
Числа справа от названий областей указывают:
1) что Eden имеет размер в 50 мегабайт, и то, что нарисовано в конце графика, последнее из значений на текущий момент — занято 25 мегабайт. Всего он может вырости до 546 мегабайт.
2) что Old может вырости до 1,333 гига, сейчас занимает 405 МБ, и забит на 145,5 МБ.
Так же для Survivor-областей и Perm Gen.
Для сравнения — вот Вам Tracer-график за 75 часов работы второго приложения, думаю, кое-какие выводы вы сможете сделать из него. Например, что активная фаза у этого приложения — с 8:30 до 17:30 в рабочие дни, и что даже на выходных оно тоже работает
Если вы вдруг увидели в своём приложении, что Old-область заполнена — попробуйте просто подождать, когда она переполнится, скорее всего она заполнена уже мусором.
Мусор — это объекты, на которые нет активных ссылок из других объектов, или целые комплексы таких объектов (например, какое-то «облако» взаимосвязанных оъектов может стать мусором, если набор ссылок указывает только на объекты внутри этого «облака», и ни на один объект в этом «облаке» ничто не ссылается «снаружи»).
Это был краткий пересказ того, что я узнал про структуру хипа за время, пока гуглил.
Предпосылки
Итак, случилось сразу две вещи:
1) после перехода на более новые библиотеки/томкеты/джавы в одну из пятниц приложение, которое я уже долгое время веду, вдруг стало вести себя из рук вон плохо спустя две недели после выставления.
2) мне на рефакторинг отдали проект, который тоже вёл себя до некоторого времени не очень хорошо.
Я уже не помню, в каком точно порядке произошли эти события, но после «чёрной пятницы» я решил наконец-то разобраться с дампами памяти детальнее, чтобы это более не было для меня чёрным ящиком. Предупреждаю, что какие-то детали я мог уже запамятовать.
По первому случаю симптомы были такие: все потоки, отвественные за обработку запросов, выжраны, на базу данных открыто всего 11 соединений, и те не сказать, что используются, база говорила, что они в состоянии recv sleep, то есть ожидают, когда же их начнут использовать.
После перезагрузки приложение оживало, но прожить могло недолго, вечером той же пятницы жило дольше всего, но уже после окончания рабочего дня таки снова свалилось. Картина всегда была одинаковой: 11 соединений к базе, и лишь один, вроде бы, что-то делает.
Память, кстати, была на минимуме. Сказать, что OOM привёл меня к поиску причин, не могу, однако полученные знания при поиске причин позволили начать активную борьбу с OOM.
Когда я открыл дамп в JVVM, из него было сложно что-либо понять.
Подсознание подсказывало, что причина где-то в работе с базой.
Поиск среди классов сказал мне, что в памяти аж 29 DataSource, хотя должно быть всего 7.
Это и дало мне точку, от которой можно было бы оттолкнуться, начать распутывать клубок.
OQL
Сидеть переклацывать в просмотровщике все эти объекты было некогда, и моё внимание наконец-то привлекла вкладка OQL Console, я подумал, что вот он, момент истины — я или начну использовать её на полную катушку, или так и забью на всё это.
Прежде, чем начать, конечно же был задан вопрос гуглу, и он любезно предоставил шпаргалку (cheat sheet) по использованию OQL в JVVM: http://visualvm.java.net/oqlhelp.html
Сначала обилие сжатой информации привело меня в уныние, но после применения гугл-фу на свет таки появился вот такой OQL-запрос:
select {instance: x, uri: x.url.toString(), connPool: x.connectionPool}
from org.apache.tomcat.dbcp.dbcp2.BasicDataSource x
where x.url != null
&& x.url.toString() == "jdbc:sybase:Tds:айпишник:порт/базаДанных"
Это уже исправленная и дополненная, финальная версия этого запроса
Результат можно увидеть на скриншоте:
После нажатия на BasicDataSource#7 мы попадаем на нужный объект во вкладке Instances:
Через некоторое время до меня дошло, что есть одно несхождение с конфигурацией, указанной в теге Resource в томкете, в файле /conf/context.xml. Ведь в дампе параметр maxTotal имеет значение 8, в то время, как мы указывали maxActive равным 20…
Тут-то до меня и начало доходить, что приложение жило с неправильной конфигурацией пула соединений все эти две недели!
Для краткости напишу тут, что в случае, если вы используете Tomcat и в качестве пула соединений — DBCP, то в 7м томкете используется DBCP версии 1.4, а в 8м томкете — уже DBCP 2.0, в котором, как я потом выяснил, решили переименовать некоторые параметры! А про maxTotal вообще на главной странице сайта написано
http://commons.apache.org/proper/commons-dbcp/
«Users should also be aware that some configuration options (e.g. maxActive to maxTotal) have been renamed to align them with the new names used by Commons Pool 2.»
Причины
Обозвал их по всякому, успокоился, и решил разобраться.
Как оказалось, класс BasicDataSourceFactory просто напросто получает этот самый Resource, смотрит, есть ли нужные ему параметры, и забирает их в порождаемый объект BasicDataSource, молча игнорируя напрочь всё, что его не интересует.
Так и получилось, что они переименовали самые весёлые параметры, maxActive => maxTotal, maxWait => maxWaitMillis, removeAbandoned => removeAbandonedOnBorrow & removeAbandonedOnMaintenance.
По умолчанию maxTotal, как и ранее, равен 8; removeAbandonedOnBorrow, removeAbandonedOnMaintenance = false, maxWaitMillis устанавливается в значение «ждать вечно».
Получилось, что пул оказался сконфигурирован с минимальным количеством соединений; в случае, если заканчиваются свободные соединения — приложение молча ждёт, когда они освободятся; и добивает всё молчанка в логах по поводу «заброшенных» соединений — то, что могло бы сразу показать, в каком именно месте
программист мудак
код хватает соединение, но не отдаёт его обратно по окончанию своей работы.
Это сейчас вся мозаика сложилась быстро, а добывались эти знания дольше.
«Так быть не должно», решил я, и запилил патчик (https://issues.apache.org/jira/browse/DBCP-435, выразился в http://svn.apache.org/viewvc/commons/proper/dbcp/tags/DBCP_2_1/src/main/java/org/apache/commons/dbcp2/BasicDataSourceFactory.java?view=markup ), патч был принят и вошёл в версию DBCP 2.1. Когда и если Tomcat 8 обновит версию DBCP до 2.1+, думаю, что админам откроются многие тайны про их конфигурации Resource
По поводу этого происшествия мне лишь осталось рассказать ещё одну деталь — какого чёрта в дампе было аж 29 DataSource’ов вместо всего 7 штук. Разгадка кроется в банальной арифметике, 7*4=28 +1=29.
Детальнее о том, почему нельзя закидывать Resource в файл /conf/context.xml томкета
На каждую подпапку внутри папки /webapps поднимается своя копия /conf/context.xml, а значит то количество Resource, которые там есть, следует умножать на количество приложений, чтобы получить общее количество пулов, поднятых в памяти томкета. На вопрос «что в этом случае делать?» ответ будет таким: нужно вынести все объявления Resource из /conf/context.xml в файл /conf/server.xml, внутрь тега GlobalNamingResources. Там Вы можете найти один, имеющийся по умолчанию, Resource name=«UserDatabase», вот под ним и размещайте свои пулы. Далее необходимо воспользоваться тегом ResourceLink, его желательно поместить в приложение, в проекте, внутрь файла /META-INF/context.xml — это так называемый «per-app context», то есть контекст, который содержит объявления компонентов, которые будут доступны только для разворачиваемого приложения. У ResourceLink параметры name и global могут содержать одинаковые значения.
Для примера:
<ResourceLink name="jdbc/MyDB" global="jdbc/MyDB" type="javax.sql.DataSource"/>
Эта ссылка будет выхватывать из глобально объявленных ресурсов DataSource с именем «jdbc/MyDB», и ресурс станет доступен приложению.
ResourceLink можно (но не нужно) разместить и в /conf/context.xml, но в этом случае доступ к ресурсам, объявленным глобально, будет у всех приложений, пусть даже и не будет столько копий DataSource в памяти.
Ознакомиться с деталями можно вот тут: GlobalNamingResources — http://tomcat.apache.org/tomcat-7.0-doc/config/globalresources.html#Environment_Entries, ResourceLink — http://tomcat.apache.org/tomcat-7.0-doc/config/globalresources.html#Resource_Links, также можно просмотреть эту страницу: tomcat.apache.org/tomcat-7.0-doc/config/context.html.
Для TC8 эти же страницы: http://tomcat.apache.org/tomcat-8.0-doc/config/globalresources.html и http://tomcat.apache.org/tomcat-8.0-doc/config/context.html .
После этого всё стало ясно: 11 соединений было потому, что в одном, активном DataSource было съедено 8 соединений (maxTotal = 8), и ещё по minIdle=1 в трёх других неиспользуемых DataSource-копиях.
В ту пятницу мы откатились на Tomcat 7, который лежал рядышком, и ждал, когда от него избавятся, это дало время спокойно во всём разобраться.
Плюс позже, уже на TC7, обнаружилась утечка соединений, всё благодаря removeAbandoned+logAbandoned. DBCP радостно сообщил в логфайл catalina.log о том, что
"org.apache.tomcat.dbcp.dbcp.AbandonedTrace$AbandonedObjectException: DBCP object created 2015-02-10 09:34:20 by the following code was never closed:
at org.apache.tomcat.dbcp.dbcp.AbandonedTrace.setStackTrace(AbandonedTrace.java:139)
at org.apache.tomcat.dbcp.dbcp.AbandonedObjectPool.borrowObject(AbandonedObjectPool.java:81)
at org.apache.tomcat.dbcp.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:106)
at org.apache.tomcat.dbcp.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
at наш.пакет.СуперКласс.getConnection(СуперКласс.java:100500)
at наш.пакет.СуперКласс.плохойПлохойМетод(СуперКласс.java:100800)
at наш.пакет.СуперКласс.вполнеВменяемыйМетод2(СуперКласс.java:100700)
at наш.пакет.СуперКласс.вполнеВменяемыйМетод1(СуперКласс.java:100600)
ещё куча строк..."
Вот этот вот плохойПлохойМетод имеет в сигнатуре Connection con, но внутри была конструкция «con = getConnection();», которая и стала камнем преткновения. СуперКласс вызывается редко, поэтому на него и не обращали внимания так долго. Плюс к этому, вызовы происходили, я так понимаю, не во время рабочего дня, так что даже если что-то и подвисало, то никому уже не было дела до этого. А в ТуСамуюПятницу просто звёзды сошлись, начальнику департамента заказчика понадобилось посмотреть кое-что
Приложение №2
Что же касается «события №2» — мне отдали приложение на рефакторинг, и оно на серверах тут же вздумало упасть.
Дампы попали уже ко мне, и я решил попробовать поковырять и их тоже.
Открыл дамп в JVVM, и «чё-то приуныл»:
Что можно понять из Object[], да ещё и в таком количестве?
( Опытный человек, конечно же, увидел уже причину, правда? )
Так у меня зародилась мысль «ну неужели никто ранее не занимался этим, ведь наверняка уже есть готовый инструмент!». Так я наткнулся на этот вопрос на StackOverflow: http://stackoverflow.com/questions/2064427/recommendations-for-a-heap-analysis-tool-for-java.
Посмотрев предложенные варианты, я решил остановиться на MAT, надо было попробовать хоть что-то, а это открытый проект, да ещё и с куда бОльшим количеством голосов, чем у остальных пунктов.
Eclipse Memory Analyzing Tool
Итак, MAT.
Рекомендую скачивать последнюю версию Eclipse, и устанавливать MAT туда, потому как самостоятельная версия MAT ведёт себя плохо, там какая-то чертовщина с диалогами, в них не видно содержимого в полях. Быть может кто-то подскажет в комментариях, чего ему не хватает, но я решил проблему, установив MAT в Eclipse.
Открыв дамп в MAT я запросил выполнение Leak Suspects Report.
Удивлению не было предела, честно говоря.
1.2 гига весят соединения в базу.
Каждое соединение весит от 17 до 81 мегабайта.
Ну и ещё «немного» сам пул.
Визуализировать проблему помог отчёт Dominator Tree:
Причиной всех падений оказались километры SQLWarning’ов, база настойчиво пыталась дать понять, что «010SK: Database cannot set connection option SET_READONLY_TRUE.», а пул соединений BoneCP не вычищает SQLWarning’и после освобождения и возврата соединений в пул (может быть это где-то можно сконфигурировать? Подскажите, если кто знает).
Гугл сказал, что такая проблема с Sybase ASE известна ещё с 2004 года: https://forum.hibernate.org/viewtopic.php?f=1&t=932731
Если вкратце, то «Sybase ASE doesn’t require any optimizations, therefore setReadOnly() produces a SQLWarning.», и указанные решения всё ещё работают.
Однако это не совсем решение проблемы, потому как решение проблемы — это когда при возврате соединения в пул все уведомления базы очищаются в силу того, что они уже никогда никому не понадобятся.
И DBCP таки умеет делать это: http://svn.apache.org/viewvc/commons/proper/dbcp/tags/DBCP_1_4/src/java/org/apache/commons/dbcp/PoolableConnectionFactory.java?view=markup, метод passivateObject(Object obj), в строке 687 можно увидеть conn.clearWarnings();, этот вызов и спасает от километров SQLWarning’ов в памяти.
Об этом я узнал из тикета: https://issues.apache.org/jira/browse/DBCP-102
Также мне подсказали про вот такой тикет в багтрекере: https://issues.apache.org/jira/browse/DBCP-234, но он касается уже версии DBCP 2.0.
В итоге я перевёл приложение на DBCP (пусть и версии 1.4). Пусть нагрузка на сервис и немаленькая (от 800 до 2к запросов в минуту), но всё же приложение ведёт себя хорошо, а это главное. И правильно сделал, потому как BoneCP уже пять месяцев не поддерживается, правда, ему на смену пришёл HikariCP. Нужно будет посмотреть, как дела в его исходниках…
Сражаемся с OOM
Впечатлившись тем, как MAT мне всё разложил по полочкам, я решил не забрасывать этот действенный инструмент, и позже он мне пригодился, потому как в первом приложении ещё остались всяческие «неучтёнки» — неучтённые вещи в коде приложения или коде хранимых процедур, которые иногда приводят к тому, что приложение склеивает ласты. Я их отлавливаю до сих пор.
Вооружившись обоими инструментами, я принялся ковырять каждый присланный дамп в поисках причин падения по OOM.
Как правило все OOM приводили меня к TaskThread.
И если нажать на надпись See stacktrace, то да, это будет как раз банальный случай, когда какой-то поток вдруг внезапно упал при попытке отмаршалить результат своей работы.
Однако здесь ничто не указывает на причину возникновения OOM, здесь лишь результат. Найти причину мне пока-что, в силу незнания всей магии OQL в MAT, помогает именно JVVM.
Загружаем дамп там, и пытаемся отыскать причину!
Искать мне следует, конечно же, именно вещи, связанные с базой данных, а посему попробуем сначала посмотреть, есть ли в памяти Statement’ы.
Два SybCallableStatement, и один SybPreparedStatement.
Думаю, что дело усложнится, если Statement’ов будет куда больше, но немного подрихтовав один из следующих запросов, указав в where нужные условия, думаю, всё у Вас получится. Плюс, конечно же, стоит хорошенько посмотреть в MAT, что за результаты пытается отмаршалить поток, какой объект, и станет понятнее, какой именно из Statement’ов необходимо искать.
select {
instance: x,
stmtQuery: x._query.toString(),
params: map(x._paramMgr._params, function(obj1) {
if (obj1 != null) {
if (obj1._parameterAsAString != null) {
return '''+obj1._parameterAsAString.toString()+''';
} else {
return "null";
}
} else {
return "null";
}
})
}
from com.sybase.jdbc4.jdbc.SybCallableStatement x
where x._query != null
Не то, это «внутренние» вызовы.
select {
instance: x,
stmtQuery: x._query.toString(),
params: map(x._paramMgr._params, function(obj1) {
if (obj1 != null) {
if (obj1._parameterAsAString != null) {
return '''+obj1._parameterAsAString.toString()+''';
} else {
return "null";
}
} else {
return "null";
}
})
}
from com.sybase.jdbc4.jdbc.SybPreparedStatement x
where x._query != null
А вот и дичь!
Для чистоты эксперимента можно кинуть такой же запрос в любимой БД-IDE, и он будет очень долго отрабатывать, а если покопаться в недрах хранимки, то будет понятно, что там просто из базы, которая нам не принадлежит, выбирается 2 миллиона строк по такому запросу с такими параметрами. Эти два миллиона даже влазят в память приложения, но вот попытка отмаршалить результат становится фатальной для приложения. Такое себе харакири.
При этом GC старательно убирает все улики, но не спасло его это, всё же источник остался в памяти, и он будет наказан.
Почему-то после всего этого рассказа почувствовал себя тем ещё неудачником.
Прощание
Вот и закончилось моё повествование, надеюсь, Вам понравилось
Хотел бы выразить благодарность своему начальнику, он дал мне время во всём этом разобраться. Считаю эти новые знания очень полезными.
Спасибо девушкам из Scorini за неизменно вкусный кофе, но они не прочтут этих слов благодарности — я даже сомневаюсь, что они знают о существовании Хабрахабра
Хотелось бы увидеть в комментариях ещё больше полезной инфы и дополнений, буду очень благодарен.
Думаю, самое время почитать документацию к MAT…
UPD1: Да, совсем забыл рассказать про такие полезные вещи, как создание дампов памяти.
docs.oracle.com/javase/7/docs/webnotes/tsg/TSG-VM/html/clopts.html#gbzrr
Опции
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/disk2/dumps
весьма полезны для генерации дампов в момент падения приложения по OutOfMemoryError,
а также существует возможность снять дамп памяти с приложения «наживо», посреди его работы.
Для этого существует утилита jmap.
Пример вызова для винды:
«C:installPSToolsPsExec.exe» -s «C:Program FilesJavajdk1.7.0_55binjmap.exe» -dump:live,format=b,file=C:dump.hprof 3440
последний параметр — это PID java-процесса. Приложение PsExec из набора PSTools позволяет запускать другие приложения с правами системы, для этого служит ключ «-s». Опция live полезна, чтобы перед сохранением дампа вызвать GC, очистив память от мусора. В случае, когда возникает OOM, чистить память незачем, там уже не осталось мусора, так что не ищите, как можно установить опцию live в случае возникновения OOM.
UPD2 (2015-10-28) | Случай номер два три
(Было принято решение дописать это сюда как апдейт, а не пилить новую статью о том же самом):
Ещё один интересный случай, но уже с Оракловой базой.
Один из проектов использует фичу с XML, проводит поиски по содержимому сохранённого XML-документа. В общем, этот проект иногда давал о себе знать тем, что вдруг внезапно один из инстансов переставал подавать признаки жизни.
Почуяв «хороший» случай потренироваться
на кошках
, я решил посмотреть его дампы памяти.
Первое, что я увидел, было «у вас тут много коннектов в памяти осталось». 21к!!! И какой-то интересный oracle.xdb.XMLType тоже давал жару. «Но это же Оракл!», вертелось у меня в голове. Забегая вперёд скажу что таки да, он виноват.
Итак, видим кучу T4CConnection, которые лежат в HashMap$Entry. Обратил внимание сразу, что вроде бы и SoftHashMap, что, вроде как, должно означать, что оно не должно вырастать до таких размеров. Но результат видите и сами — 50-60 килобайт в коннекте, и их реально МНОГО.
Посмотрев, что собой представляют HashMap$Entry — увидел, что примерно картина одинакова, всё связано с SoftHashMap, с Оракловыми коннектами.
Что, собственно, подтверждалось такой картинкой. HashMap$Entry было просто море, и они более-менее сакуммулировались внутри oracle.xdb.SoftHashMap.
В следующем дампе картина была примерно такой же. По Dominator Tree было видно, что внутри каждого Entry находится тяжёлый такой BinXmlProcessorImpl.
-=-=-
Если учесть, что я в тот момент был не силён в том, что такое xdb, и как он связан с XML, то, несколько растерявшись, я решил, что надо бы погуглить, быть может кто-то уже в курсе, что со всем этим нужно делать. И чутьё не обмануло, по запросу «oracle.xdb.SoftHashMap T4CConnection» нашлось
раз piotr.bzdyl.net/2014/07/memory-leak-in-oracle-softhashmap.html
и два leakfromjavaheap.blogspot.com/2014/02/memory-leak-detection-in-real-life.html
Утвердившись, что тут всё-таки косяк у Оракла, дело оставалось за малым.
Попросил администратора БД посмотреть информацию по обнаруженной проблеме:
xxx: Ключевые слова: SoftHashMap XMLType
yyy: Bug 17537657 Memory leak from XDB in oracle.xdb.SoftHashMap
yyy: The fix for 17537657 is first included in
12.2 (Future Release)
12.1.0.2 (Server Patch Set)
12.1.0.1.4 Database Patch Set Update
12.1.0.1 Patch 11 on Windows Platforms
yyy: нда. Описание
Description
When calling either getDocument() using the thin driver, or getBinXMLStream()
using any driver, memory leaks occur in the oracle.xdb.SoftHashMap class.
BinXMLProcessorImpl classes accumulate in this SoftHashMap, but are never
removed.
xxx: Всё так и есть
Вот описание фикса: updates.oracle.com/Orion/Services/download?type=readme&aru=18629243 (для доступа требуется учётка в Оракл).
-=-=-
После применения фикса инстансы нашего приложения живут уже месяц, и пока без эксцессов. *постучал по дереву* *поплевал через левое плечо*
Успехов Вам в поисках!