I'm executing webServiceTemplate.marshalSendAndReceive in my multithreaded app (10 threads) and it leads memory leak with Finalizer class. I have found which classes uses finalize in its implementetion while debugging. In in most cases it is UnmarshallerImpl and HttpsURLConnectionImpl. Jaxb2Marshaller creates new instanse of UnmarshallerImpl for every request, and as I found out, GC can't delete this objects, until low priority thread execute finalize() for each instanse. Apparently, FinalizerThread can't process the queuq of this object in time and does not free memory. When I mock method which calls webServiceTemplate.marshalSendAndReceive the problem with memory leak goes away goes away. Is there way to solve this problem?
Here is my WebServise configuration:
@Configuration
public class WebServiceConfig {
private final Props props;
private final ApplicationProps applicationProps;
public WebServiceConfig(@NotNull final Props props, @NotNull final ApplicationProps applicationProps) {
this.props = props;
this.applicationProps = applicationProps;
}
@Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setPackagesToScan(applicationProps.getPackagesToScan());
return marshaller;
}
@Bean
public WebServiceTemplate webServiceTemplate(final WebServiceTemplateBuilder builder, final AllTimeouts allTimeouts) {
return builder
.setMarshaller(marshaller())
.setUnmarshaller(marshaller())
.messageSenders(new BasicAuthHttpsConnectionMessageSender(
props.getUsername(),
props.getPassword(),
allTimeouts.getReadTimeout()))
.build();
}
}
public class BasicAuthHttpsConnectionMessageSender extends HttpsUrlConnectionMessageSender {
private final String b64Creds;
public BasicAuthHttpsConnectionMessageSender(final String username,
final String password,
final Integer readTimeout) {
byte[] message;
message = String.format("%s:%s", username, password).getBytes(StandardCharsets.UTF_8);
b64Creds = DatatypeConverter.printBase64Binary(message);
setReadTimeout(Duration.ofMillis(readTimeout));
}
@Override
protected void prepareConnection(final HttpURLConnection connection) throws IOException {
connection.setRequestProperty(HttpHeaders.AUTHORIZATION, String.format("Basic %s", b64Creds));
super.prepareConnection(connection);
}
}
Here is my dependensies:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-web-services'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation group: 'javax.validation', name: 'validation-api', version: '2.0.1.Final'
implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
implementation group: 'net.logstash.logback', name: 'logstash-logback-encoder', version: '4.10'
compile('javax.xml.bind:jaxb-api:2.3.0')
compile('javax.activation:activation:1.1')
compile('org.glassfish.jaxb:jaxb-runtime:2.3.0')
compile 'org.hibernate.validator:hibernate-validator:6.0.13.Final'
compile group: 'org.postgresql', name: 'postgresql', version: '42.2.11'
compile group: 'org.springframework.vault', name: 'spring-vault-core', version: '2.1.3.RELEASE'
compile group: 'org.springframework.ws', name: 'spring-ws-security', version: '2.2.2.RELEASE'
compile group: 'org.springframework.ws', name: 'spring-ws-support', version: '2.2.2.RELEASE'
compile group: 'org.springframework.boot', name: 'spring-boot-configuration-processor', version: '2.1.8.RELEASE'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}