问题描述
我一直在通过浸泡测试运行具有弹簧安全性的Spring Boot应用程序,发现它逐渐填满了内存分配.
I have been running a spring boot application with spring security under a soak test and found that it gradually fills up its memory allocation.
我通过以下方式启动了应用程序:
I booted up the application with:
java -Xmx128m -XX:+ PrintGCDetails -XX:+ PrintGCTimeStamps -Xloggc:gc.log -verbose:gc -jar target/myapp-0.0.1-SNAPSHOT.jar
java -Xmx128m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log -verbose:gc -jar target/myapp-0.0.1-SNAPSHOT.jar
这样我就可以获取gc信息,并限制内存更快地达到我的OutOfMemoryError情况(半小时,而不是两周)
So that I could get teh gc info out and restrict the memory to reach my OutOfMemoryError situation sooner (well half an hour, rather than two weeks)
我已经使用tomcat和jetty作为容器运行了该容器,然后运行了一个bash脚本,该脚本在服务器上激发了很多cURL,以模拟生产中的负载.我将jmap指向该过程,并在崩溃前不久得到了以下信息(仅显示了前40个结果,并显示了tomcat的运行情况)
I have run this with both tomcat and jetty as the container and then ran a bash script that fired a whole lot of cURLs at the server to simulate the sort of loads in production. I pointed jmap at the process and got the following shortly before the crash (just showing the top 40 results, and for the tomcat run)
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之前,这两种方法的数量一直在增长.都没有被收集.
As you can see there are heaps of tomcat StandardSessions in operation, but also a lot of OAuth2Authentication instances (far more than the 2 or 3 I would have expected). The numbers of both of these just keep growing until the OutOfMemoryError occurs. Neither are ever collected.
我实现了下面提供的spring安全配置
I implemented a spring security configuration which is provided below
@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();
}
}
}
我也有下面提供的过滤器
I also have the filter, also provided below
@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)会话?
Does anyone know what the source of the memory leak is? why do I have so many Tomcat (or Jetty) session open?
很显然,这里的房间有限,但是如果需要任何进一步的信息,我很乐意为您服务.
Obviously, there is limited room here, but if any further information is needed, I am happy to oblige.
谢谢
推荐答案
您在这里和我们一样.看看@ https://github.com/spring-projects/spring-boot /issues/2084
You're experiencing same as we here. Take a look @ https://github.com/spring-projects/spring-boot/issues/2084
这篇关于Spring Boot或Spring Security内存可能泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!