-
Bug
-
Resolution: Fixed
-
Major
-
Jenkins Core > 2.61.X
The latest version of Jenkins core uses Jetty 9.4, this version seems changes the way to manage the session cache and now it is infinite by default, this makes that the session cache is not cleaned with the time and cause a memory leak.
This script gets the EvictionPolicy value and returns -1
import net.bull.javamelody.* def sessionMapField = SessionListener.class.getDeclaredField('SESSION_MAP_BY_ID') sessionMapField.setAccessible(true) def sessions = sessionMapField.get(null) for (def sessionKV : sessions) { def session = sessionKV.value println session.getSessionHandler().getSessionCache().getEvictionPolicy() }
Jetty was upgraded from 9.2 to 9.4 in Jenkins 2.61, The old Jetty uses a different caching mechanism, which seems to have a non-infinite default eviction policy
The workaround is to place this script at JENKINS_HOME/init.groovy.d to set the eviction policy to 30 min.
import net.bull.javamelody.* def sessionMapField = SessionListener.class.getDeclaredField('SESSION_MAP_BY_ID') sessionMapField.setAccessible(true) def sessions = sessionMapField.get(null) for (def sessionKV : sessions) { def session = sessionKV.value session.getSessionHandler().getSessionCache().setEvictionPolicy(1800) }
These are other utility scripts to check the leak.
This script list the sessions
import net.bull.javamelody.* println SessionListener.getSessionCount() + " sessions:" def sessioninfos = SessionListener.getAllSessionsInformations() for (sessioninfo in sessioninfos) { println sessioninfo }
This script returns the number of sessions in cache
import net.bull.javamelody.* def sessionMapField = SessionListener.class.getDeclaredField('SESSION_MAP_BY_ID') sessionMapField.setAccessible(true) def sessions = sessionMapField.get(null) for (def sessionKV: sessions) { def session = sessionKV.value def sessionCache = session.getSessionHandler().getSessionCache() def sessionsField = sessionCache.class.getDeclaredField('_sessions') sessionsField.setAccessible(true) println sessionsField.get(sessionCache).size() }
This script force to clear the session cache
import net.bull.javamelody.* def sessionMapField = SessionListener.class.getDeclaredField('SESSION_MAP_BY_ID') sessionMapField.setAccessible(true) def sessions = sessionMapField.get(null) for (def sessionKV: sessions) { def session = sessionKV.value def sessionCache = session.getSessionHandler().getSessionCache() def sessionsField = sessionCache.class.getDeclaredField('_sessions') sessionsField.setAccessible(true) def sessionKeys = sessionsField.get(sessionCache).keys() for (def sessionKey: sessionKeys) { sessionCache.delete(sessionKey) } }