2015-12-28 10 views
5

Dziś pojawia się błąd PermGen OutOfMemory. PokazaneWyczerpany zatrzymaj logback w środowisku kontenera

Analiza że Najbliższe GC głównej do WebappClassLoader jest Logback gwint:

this  - value: org.apache.catalina.loader.WebappClassLoader #4 
    <- contextClassLoader (thread object)  - class: java.lang.Thread, value: org.apache.catalina.loader.WebappClassLoader #4 

który jest: zrzutu

java.lang.Thread#11 - logback-1 

gwintu z hałdy ładunkową do tego wątku:

"logback-1" daemon prio=5 tid=34 WAITING 
at sun.misc.Unsafe.park(Native Method) 
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186) 
at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:458) 
at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:359) 
    Local Variable: java.util.concurrent.SynchronousQueue$TransferStack$SNode#1 
    Local Variable: java.util.concurrent.SynchronousQueue$TransferStack#6 
at java.util.concurrent.SynchronousQueue.take(SynchronousQueue.java:925) 
    Local Variable: java.util.concurrent.SynchronousQueue#6 
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) 
    Local Variable: java.util.concurrent.ThreadPoolExecutor#34 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) 
    Local Variable: java.util.concurrent.ThreadPoolExecutor$Worker#11 
at java.lang.Thread.run(Thread.java:745) 

Używam Tomcat 8 z hot feepem redeploy e reloadable="true" i externalized CLASSPATH poprzez PreResources:

<Context docBase="/home/user/devel/app/src/main/webapp" 
     reloadable="true"> 
    <Resources> 
     <!-- To override application.properties and logback.xml --> 
     <PreResources className="org.apache.catalina.webresources.DirResourceSet" 
         base="/home/user/devel/app/.config" 
         internalPath="/" 
         webAppMount="/WEB-INF/classes" /> 
    </Resources> 
</Context> 

i logback.xml z scan="true":

<configuration debug="false" scan="true" scanPeriod="5 seconds"> 
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> 
    ... 

Po zapisaniu zmian w /home/user/devel/app/.config/logback.xml Tomcat 8 otrzymywać powiadomienia (nie jestem pewien, co API wykorzystywane do monitorowania zmian na FS) rozpoczęto ponowne wdrażanie aplikacji. Tak dzieje się przed PermGen OutOfMemory.

Jak mogę z gracją zatrzymać logback w środowisku kontenerowym?

Jak zatrzymać wątek "logback-1"?

znalazłem kilka powiązanych dyskusji, ale nie mogę zrozumieć, co zrobić z tą informacją:

UPDATE Gram z zrzutem sterty w visualvm. Pod poziomem skoku referencyjnym od złej logback-1 wątku:

lvl1 = flatten(filter(referees(heap.findObject(0xf4c77610)), "!/WebappClassLoader/(classof(it).name)")) 

lvl2 = flatten(map(lvl1, "referees(it)")) 

lvl3 = flatten(map(lvl2, "referees(it)")) 

odnosić się do

ch.qos.logback.core.util.ExecutorServiceUtil$1 

Przez grepping w źródłach Logback dla ExecutorServiceUtil I found changelog entry:

wszystkie wątki otwierane przez ch. qos.logback.core.util.ExecutorServiceUtil # THREAD_FACTORY to teraz demony, które naprawiają aplikację zawieszoną przy wyłączaniu, gdy LoggerContext # stop() nie jest wywoływany (LOGBACK-929). Zauważ, że wątki daemon zostały nagle przerwane przez maszynę JVM, co może spowodować, że niepożądane wyniki, takie jak uszkodzone pliki napisane przez FileAppender, mogą spowodować niepowodzenie. Nadal wysoce zalecane jest dla aplikacji wywoływanie LoggerContext # stop() (np. W haku wyłączania) na z gracją zamykającymi aplikatory.

Czy to prawda, że ​​wątki demona w środowisku kontenerowym są niebezpieczne i prowadzą do przecieków pamięci?

Odpowiedz

5

Nie do końca rozumiem, co powinienem zrobić. Obecnie usunąć jul-to-slf4j most od projektu pom.xml i ten wiersz:

<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"/> 

z logback.xml. Nawet z tą linią aplikacja nie ma wątku "logback-1".

As suggest official docs zarejestrować:

public class ShutdownCleanupListener implements ServletContextListener { 
    @Override 
    public void contextInitialized(ServletContextEvent sce) { } 

    @Override 
    public void contextDestroyed(ServletContextEvent sce) { 
     if (LoggerFactory.getILoggerFactory() instanceof LoggerContext) { 
      ((LoggerContext) LoggerFactory.getILoggerFactory()).stop(); 
     } 
    } 
} 

w web.xml:

<listener> 
    <listener-class>com.app.servlet.ShutdownCleanupListener</listener-class> 
</listener> 

Aby usunąć bezpośrednią zależność od:

import ch.qos.logback.classic.LoggerContext; 

odbicie może być stosowany.

Nie jestem pewien, czy robię dobrze. Zobaczę, czy dostaję błąd PermGen OutOfMemory z powodu Logback.

UPDATE Po odkryli zależność odniesienia z ExecutorServiceUtil klasy I sprawdzone źródła Logback i okazało się, że ta klasa tworzyć wątki o nazwach takich jak złe powyżej:

thread.setName("logback-" + threadNumber.getAndIncrement()); 

Ta klasa używać ch.qos.logback.core.ContextBase i nici odchylił się wewnątrz:

public void stop() { 
    // We don't check "started" here, because the executor service uses 
    // lazy initialization, rather than being created in the start method 
    stopExecutorService(); 
    started = false; 
} 

Zauważ, że LoggerContext jest podklasą ContextBase tak powyżej s olacja naprawdę naprawia mój problem.

+0

Dzięki to naprawić mój WebappClassLoader przeciek, jak dobrze! – whitestryder

1

Czy to prawda, że ​​wątki demona w środowisku kontenerowym są niebezpieczne i prowadzą do wycieków pamięci?

Oświadczenie o wątków demon odnoszą się do przypadku samodzielna aplikacja (jak w bug report), gdzie rzekomo JVM jest zamknięty, gdy aplikacja jest zakończone. Wątek inny od demona uniemożliwia wyłączenie maszyny JVM.

W kontekście JavaEE, w którym JVM serwera aplikacji pozostaje taki sam w przypadku wielu cyklów życia wielu aplikacji, demon a nie demon nie ma wpływu na fakt, że live threads are GC roots.