我一直在通过浸泡测试运行带有弹簧安全性的spring boot应用程序,发现它会逐渐填满其内存分配。
我用以下命令启动了该应用程序:
java -Xmx128m -XX:+ PrintGCDetails -XX:+ PrintGCTimeStamps -Xloggc:gc.log -verbose:gc -jar target / myapp-0.0.1-SNAPSHOT.jar
这样我就可以获取gc信息,并限制内存更快地达到OutOfMemoryError的情况(半小时,而不是两周)
我已经使用tomcat和jetty作为容器来运行它,然后运行了一个bash脚本,该脚本在服务器上激发了很多cURL,以模拟生产中的负载。我将jmap指向该过程,并在崩溃前不久得到了以下信息(仅显示了前40个结果,并显示了tomcat的运行情况)
num #instances #bytes class name ---------------------------------------------- 1: 395984 32564344 [C 2: 388697 9328728 java.lang.String 3: 61258 5915088 [B 4: 100297 4814256 java.util.HashMap 5: 50892 4478496 org.apache.catalina.session.StandardSession 6: 58774 3656824 [Ljava.util.HashMap$Node; 7: 84773 3390920 java.util.TreeMap$Entry 8: 51522 3339304 [Ljava.util.Hashtable$Entry; 9: 51834 3317376 java.util.concurrent.ConcurrentHashMap 10: 102111 3267552 java.util.HashMap$Node 11: 96256 3080192 java.util.concurrent.ConcurrentHashMap$Node 12: 24101 2754560 [Ljava.util.concurrent.ConcurrentHashMap$Node; 13: 51472 2470656 java.util.Hashtable 14: 55102 2204080 java.util.LinkedHashMap$Entry 15: 83020 1992480 java.util.ArrayList 16: 34353 1923768 java.util.LinkedHashMap 17: 59156 1892992 org.springframework.boot.loader.util.AsciiBytes 18: 29574 1656144 org.springframework.boot.loader.jar.JarEntryData 19: 18029 1586552 java.lang.reflect.Method 20: 28391 1562080 [Ljava.lang.Object; 21: 37178 1487120 java.lang.ref.SoftReference 22: 47648 1446600 [I 23: 52337 1256088 java.lang.Long 24: 26134 1254432 java.util.TreeMap 25: 50904 1221696 java.beans.PropertyChangeSupport 26: 11777 1214464 java.lang.Class 27: 23748 1139904 org.springframework.security.oauth2.provider.OAuth2Request 28: 35994 863856 java.util.Collections$UnmodifiableRandomAccessList 29: 50904 814464 java.beans.PropertyChangeSupport$PropertyChangeListenerMap 30: 50892 814272 org.apache.catalina.session.StandardSessionFacade 31: 49748 795968 java.util.HashSet 32: 24066 770112 java.util.Collections$UnmodifiableMap 33: 23748 759936 org.springframework.security.oauth2.provider.OAuth2Authentication 34: 23748 759936 org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails 35: 26000 624000 javax.management.openmbean.CompositeDataSupport 36: 12015 576664 [Ljava.lang.String; 37: 16319 522208 com.sun.org.apache.xerces.internal.xni.QName 38: 15288 489216 java.lang.ref.WeakReference 39: 26448 423168 java.util.LinkedHashSet 40: 26011 416176 java.util.TreeMap$KeySet
如您所见,有许多正在运行的tomcat StandardSessions堆,还有很多OAuth2Authentication实例(远远超过我预期的2或3个实例)。这两个数字一直保持增长,直到发生OutOfMemoryError为止。都没有被收集。
我实现了下面提供的spring安全配置
@Configuration @ComponentScan("com.xxx.xxxxx") public class TokenConfig { private static final String RESOURCE_ID = "touchAuth"; @Configuration @EnableResourceServer protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(ResourceServerConfiguration.class); @Autowired TouchUserDetailsService touchUserDetailsService; @Autowired TouchUserAuthenticationFilter touchUserAuthenticationFilter; @Override public void configure(ResourceServerSecurityConfigurer resources) { resources .resourceId(RESOURCE_ID); } @Override public void configure(HttpSecurity http) throws Exception { //@formatter: off http .addFilterBefore(touchUserAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) .requestMatchers() .antMatchers("/**/secure/**", "/basket/**", "/transactions/**", "/support-requests/**", "/devices/resources/**", "/devices/information/**", "/devices/support-request", "/retailers/resources/**", "/news-items-display/last") .and() .authorizeRequests() .anyRequest() .authenticated(); //@formatter: on } } @Configuration @EnableAuthorizationServer protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { @Autowired DataSource dataSource; @Autowired private JdbcTokenStore jdbcTokenStore; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .tokenStore(jdbcTokenStore); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients .jdbc(dataSource); } } @Configuration @EnableWebSecurity protected static class SecurityConfig extends WebSecurityConfigurerAdapter{ @Autowired private TxxxxxUserDetailsService txxxxxUserDetailsService; @Autowired DataSource dataSource; @Bean public JdbcTokenStore jdbcTokenStore() { JdbcTokenStore jdbcTokenStore = new JdbcTokenStore(dataSource); return jdbcTokenStore; } @Bean public AuthenticationManager authenticationManagerBean() throws Exception { DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); authenticationProvider.setUserDetailsService(touchUserDetailsService); List<AuthenticationProvider> authenticationProviders = new ArrayList<>(); authenticationProviders.add(authenticationProvider); ProviderManager providerManager = new ProviderManager(authenticationProviders); return providerManager; } @Override protected void configure(HttpSecurity http) throws Exception { http .headers().disable() // allow things to be displayed in iframes, for example. .csrf().disable() .authorizeRequests() .antMatchers("/favicon.ico").permitAll() .antMatchers("/css/*").permitAll() .antMatchers("/images/*").permitAll() .antMatchers("/error*").permitAll() .antMatchers("/ping").permitAll() .antMatchers("/info").permitAll() .antMatchers("/").permitAll() .antMatchers("/content/**").permitAll() .antMatchers("/devices/commission").permitAll() .antMatchers("/devices/device-import").permitAll() .antMatchers("/devices/list").permitAll() .antMatchers("/devices/modify").permitAll() .antMatchers("/news-items-management/**").permitAll() .antMatchers("/news-items-display/*").permitAll() .antMatchers("/support-request-management/*").permitAll() .anyRequest() .authenticated(); } } }
我也有过滤器,也在下面提供
@Component public class TxxxxUserAuthenticationFilter extends AbstractAuthenticationProcessingFilter { private static final Logger LOG = LoggerFactory.getLogger(TxxxxUserAuthenticationFilter.class); public static final String FILTER_PROCESS_URL = "/login"; public static final String X_STANDALONE = "X_STANDALONE"; public static final String YES = "YES"; protected TxxxxUserAuthenticationFilter() { super(FILTER_PROCESS_URL); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { LOG.debug("attemptAuthentication invoked"); String username = request.getParameter("username"); String password = request.getParameter("password"); if (username == null) { username = ""; } if (password == null) { password = ""; } username = username.trim(); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); Authentication authentication = this.getAuthenticationManager().authenticate(authRequest); request.getSession().setAttribute("SESSION_AUTHENTICATED", authentication); return authentication; } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) req; SecurityContext securityContext = (SecurityContext) httpServletRequest.getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY); if (isLoginRequest(httpServletRequest)) { super.doFilter(req, res, chain); return; } // If the Authorization header is absent, the framework treats it as a non-oauth request and continues the security chain. if (securityContext != null && ((HttpServletRequest) req).getHeader("Authorization") == null) { securityContext.setAuthentication(null); } // Throw an exception if standalone device and a valid authentication is not found. if (requiresFormAuthentication(httpServletRequest) ) { Authentication authentication = (Authentication)httpServletRequest.getSession().getAttribute("SESSION_AUTHENTICATED"); if(authentication == null || !authentication.isAuthenticated()) { throw new AuthenticationCredentialsNotFoundException("Access denied"); } } chain.doFilter(req, res); } private boolean doesAuthExistsInContext(SecurityContext securityContext) { return securityContext != null && securityContext.getAuthentication() != null; } private boolean requiresFormAuthentication(HttpServletRequest httpServletRequest) { return YES.equalsIgnoreCase(httpServletRequest.getHeader(X_STANDALONE)); } private boolean isLoginRequest(HttpServletRequest req) { return FILTER_PROCESS_URL.equalsIgnoreCase(req.getRequestURI()); } @Autowired public void setAuthenticationManagerBean (AuthenticationManager authenticationManager) { super.setAuthenticationManager(authenticationManager); } }
有谁知道内存泄漏的根源是什么?为什么我要打开这么多的Tomcat(或Jetty)会话?
显然,这里的房间有限,但是如果需要任何其他信息,我很乐意为您服务。
谢谢
您在这里和我们一样。看看@ https://github.com/spring-projects/spring- boot/issues/2084