我在浸泡测试中运行了一个带弹簧安全性的spring启动应用程序,发现它逐渐填满了内存分配。
我启动了应用程序:
java -Xmx128m -XX:+ PrintGCDetails -XX:+ PrintGCTimeStamps -Xloggc:gc.log -verbose:gc -jar target / myapp-0.0.1-SNAPSHOT.jar
这样我就可以获取信息并限制内存更快地达到我的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。两者都没有被收集。
我实现了一个弹簧安全配置,在下面提供
@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)会话?
显然,这里的空间有限,但如果需要进一步的信息,我很乐意帮忙。
感谢