- Solve OutOfMemoryError UncaughtExceptionHandler 2016-07-08 07:40
- About me
- Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread «Thread-5» [duplicate]
- OutOfMemoryError: поймай, если сможешь
- Предистория
- ООМ своими руками
- Обычный код
- Лови Throwable — говорили они
- Потерянный OutOfMemoryError
- UncaughtExceptionHandler — не панацея
- Финализируй это
- Ода функциональному программированию
- Как надо делать
- Выводы
Solve OutOfMemoryError UncaughtExceptionHandler 2016-07-08 07:40
In this page I will show you how to solve Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread . This error means there is not enough memory to run your application. What you need to do is increasing memory. The key question is there are a lot of arguments for java which is right?
short answer
Add arguments like following. The value of PermSize you can tune by your own environment.
-XX:PermSize=60M -XX:MaxPermSize=512M
long answer
You’ve seen this error before: java.lang.OutOfMemoryError: PermGen space and you know how to solve it. Just increase the value of PermSize . I write a simple example to help me understand this error.
/** * -XX:PermSize=10M -XX:MaxPermSize=10M */ public class JavaMethodAreaOOM < static class OOMObject < >public static void main(String[] args) < while (true) < Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOMObject.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() < @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable < return methodProxy.invoke(o, objects); > >); enhancer.create(); > > >
Use cglib to generate class you need add the dependence, the pom file like following.
dependency> groupId>cglibgroupId> artifactId>cglibartifactId> version>2.2.2version> dependency>
Add the arguments -XX:PermSize=10M -XX:MaxPermSize=10M before run this example. I run this example in different jdk and get different result.
Exception in thread "main" net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:237) at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377) at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285) at com.henryxi.jvm.JavaMethodAreaOOM.main(JavaMethodAreaOOM.java:33) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144) Caused by: java.lang.reflect.InvocationTargetException at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:384) at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:219) . 8 more Caused by: java.lang.OutOfMemoryError: PermGen space at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631) at java.lang.ClassLoader.defineClass(ClassLoader.java:615) . 13 more
Exception in thread "main" Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"
After reading this blog About G1 Garbage Collector, Permanent Generation and Metaspace I understand. PermGen supported in jdk6 and jdk7 but not supported in jdk8. But in jdk7 when PermGen is not enough it won’t throw «OutOfMemoryError: PermGen space» it will throw «OutOfMemoryError thrown from the UncaughtExceptionHandler». Hotspot is giving up «Permanent Generation» step by step it use «Metaspace» instead. You can learn the detail in above link.
About me
A java developer like programing and writing. Hope my blog can help you.
Welcome to leave comments.
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread «Thread-5» [duplicate]
While doing maven build I am receiving following error. Does anybody know how to resolve it? Exception in thread «main» Exception in thread «Thread-1» Exception in thread «Thread-7» Exception in thread «Thread-3» Exception in thread «Thread-4» Exception in thread «Thread-2» Exception in thread «Thread-5» Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread «main» Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread «Thread-1» Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread «Thread-7» Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread «Thread-3» Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread «Thread-4» Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread «Thread-2» Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread «Thread-5»
Results : Tests run: 100, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] --------------------------------------------------------------------- [INFO] Total time: 01:14 min [INFO] Finished at: 2018-02-11T00:12:46+01:00 [INFO] Final Memory: 87M/704M [INFO] --------------------------------------------------------------------- [ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.19.1:test (default-test) on project coba.zaa.engine.workflow: Execution default-test of goal org.apache.maven.plugins:maven-surefire-plugin:2.19.1:test failed: The forked VM terminated without properly saying goodbye. VM crash or System.exit called? [ERROR] Command was cmd.exe /X /C "C:\xxxxx\Tools\Release2\JDK\jdk_1.7.0.80_x64\jre\bin\java -jar C:\vvv\ZAA\nnnn\mmmmmm\target\surefire\surefirebooter1494547892126715647.jar C:\vvv\ZAA\nnnn\mmmmmm\\target\surefire\surefire2662483945733244110tmp C:\vvv\ZAA\nnnn\mmmmmm\target\surefire\surefire_05005140060060096785tmp" [ERROR] -> [Help 1] [ERROR] http://cwiki.apache.org/confluence/display/MAVEN/PluginExecutionException
OutOfMemoryError: поймай, если сможешь
Всем привет! Сегдня хотел бы поделиться опытом обратоки ошибки ООМ. Эту статью меня побудила написать проблема, с которой я столкнулся. И которая, как позже выяснилось, долгое время оставалсь незамеченой. Меня заинтересовал этот вопрос, так что я решил изучить его немного глубже.
Предистория
У нас есть сервис, который по расписани закидывает задачу по обработке данных в ExecutorService. Это достаточно тяжелая задача. И в один прекрасный момент информации просто стало больше и она не влезла в наш -Xmx.
ООМ своими руками
Для тестирования мне необходимо забить всю память объектами, которые GC будет считать живыми. Для этого я использовал следующий код:
public class MemoryGrabber < static final List
Тут тоже присутствует некоторая проблема, но об этом позже.
Обычный код
public class BadExecutor < private static final Logger logger = LogManager.getLogger(BadThread.class); private static final ExecutorService executor = Executors.newFixedThreadPool(5); public static void main(String[] args) throws Exception < executor.submit(() -> < try < grabAllMemory(); >catch (Exception e) < logger.error(e.getMessage()); >>); > >
Этот код вроде бы выглядит неплохо, ничего особенного тут нет. Наверное, многие писали что-то подобное не один раз. Но проблема в том, что прм ООМ, не будет выведено вообще ничего. Ни в лог ни в поток вывода.
Лови Throwable — говорили они
Да точно, ведь OutOfMemoryError — это Error, а не Exception. Поэтому тут он успешно прлетает мимо catch блока и перехватывается уже в коде ThreadPoolExecutor. Где проглатывается, а сам поток начинает ждать новой задачи. Всем известно, что в самой корневой точке кода лучше ловить Throwable.
К сожалению, если вместо Exception в данной ситуации поймать Throwable, ничего не изменится. При вызове logger.error(), мы просто получим новый ООМ, который так же канет в недрах ThreadPoolExecutor.
Стоит заметить, что если бы вместо ExecutorService создавался бы новый Thread, то все ошибки в конечном счете были бы обработаны UncaughtExceptionHandler в случае смерти потока, и в stderr была бы информация. ThreadPoolExecutor же пытается переиспользовать потоки, что в принципе ожидаемо.
Потерянный OutOfMemoryError
Закидывая задачу в ExecutorService, мы забыли очень важную вещь — воспользоваться Future, который возвращает метод submit().
public class GetFuture < private static final Logger logger = LogManager.getLogger(BadThread.class); private static final ExecutorService executor = Executors.newFixedThreadPool(5); public static void main(String[] args) throws Exception < try < executor.submit(MemoryGrabber::grabAllMemory).get(); >catch (Throwable e) < logger.error(e); >> >
Теперь стало немного лучше. Если logger.error() выкинет новый ООМ, то main поток свалится и, возможно, выведет ошибку. Это помогает вытащить результат из ExecutorService наружу. Все видели что-то подобное:
Exception in thread "main" Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"
Это сообщение выводит обработчик ошибок по умолчанию, который вызывается в случае непредвиденной смерти потока.
UncaughtExceptionHandler — не панацея
Не стоит радоваться раньше вреени, т.к. лучше стало совсем немного. Если не переопределить обработчик, то вызывается ThreadGroup.uncaughtException(), в котором есть следующий код:
System.err.print("Exception in thread \"" + t.getName() + "\" "); e.printStackTrace(System.err);
Первая же строка создает новый объект при помощи конкатенации и, если там не вылетит новый ООМ, то есть большая вероятность получить его в printStackTrace(). Тут все зависит от обстоятельств. Но суть в том, что даже получив ООМ в главном потоке, есть шанс ничего о нем не узнать.
Финализируй это
Итак, теперь наша проблема в том, что нет памяти для логирования. Из-за чего получаем вторую ошибку. Так может быть попробуем освободить пространство? Проблема заключчается в том, что MemoryGrabber.array — статическая переменная. И объекты доступные через нее GC считает живыми. Попробую ее почистить.
public class FinalizeIt < private static final Logger logger = LogManager.getLogger(BadThread.class); private static final ExecutorService executor = Executors.newFixedThreadPool(5); public static void main(String[] args) throws Exception < try < executor.submit(() -> < try < grabAllMemory(); >finally < MemoryGrabber.arrays.clear(); // Очищаем память >>).get(); > catch (Throwable e) < logger.error(e); >executor.shutdownNow(); > >
Теперь во время логирования будет вызван сборщик мусора, который уже сможет удалить ненужную структуру данных.
Ода функциональному программированию
Вначале я сказал, что в MemoryGrabber есть проблема. Она заключается в статической переменной array. Дело в том, что эта переменная продолжает жить после того момента, как все свалилось с ошибкой. Очевыдным костылем является ее обнуление в блоке finaly. Было бы намного лучше, если она хранилась на стеке вызова.
public class FunctionalGrabber < public static void grabAllMemory() < List
Теперь нашь лист List превратится в мусор как только завершится метод grabAllMemory. Не важно, с ошибкой или без. Почти Scala.
Как надо делать
Надеюсь, мне удалось донести мысль о том, что попытки поймать и обработать OutOfMemoryError в коде — сомнительная затея по ряду причин. Для этих целей лучше полагаться на следующие параметры JVM:
- -XX:+HeapDumpOnOutOfMemoryError и -XX:HeapDumpPath — сгенерируют дамп кучи во время ООМ, даже если приложение осталось работать
- -XX:+ExitOnOutOfMemoryError и -XX:ExitOnOutOfMemoryErrorExitCode — позволяют просто завершить процесс с определенным кодом
- -XX:+CrashOnOutOfMemoryError — остановит с ошибкой и создаст лог JVM
Последние два параметра появились только в JDK 8u92, остальные еще в 1.4. Оптимальным поведением является завершение процесса в случае OutOfMemoryError. Такая логика — самая понятная для всех разработчиков и тех, кто будет поддерживать приложение. Попытки обработать подобные ошибки могут привести к последствиям, неочевидным даже для самого автора.
Выводы
В статье я постарался разобраться в некоторых ошибках, из-за которых могут возникнуть проблемы при появлении ООМ. Чтобы их избежать, нужно иметь в виду:
- Процесс нужно правильно конфигурировать, чтобы завершить его работу, если память закончится
- При определенных условиях можно не получить явных доказательст ООМ, даже если есть логирование и вывод перенаправлен в файл
- Можно попытаться отловить OutOfMemoryError и продолжить работу, технически это возможно, но о таком код называют дурно пахнущим.