Severe memory leak when setUseEngineSocketByDefault(true)
- Dominant language
- Java
- Stars
- 1.4k
- Forks
- 326
- Avg merge
- 16h 22m
- Merged PRs (30d)
- 17
Description
I've included below the source code of a simple test that creates 100k localhost SSL connections.
If it is run without conscrypt, it uses approx 330MB of Linux res mem. With conscrypt, it uses approx 3.7GB res mem, despite Xmx being set to 512M.
I've tested this both on Ubuntu 18 and Centos 7, on two different machines. One machine is a Ubuntu VM with 2 virtual cores on an Apple laptop with 6 cores, and the other machine has dual Xeons, each with 8 cores. Both tests were with OpenJDK 14.0.1 as downloaded from https://jdk.java.net/14/ and with conscrypt-openjdk-2.4.0-linux-x86_64 from the maven repo.
(Note that this memory leak happens when testing with both conscrypt 2.4.0 and 2.2.1.)
To make the code run, a keystore must first be created using the command: `keytool -genkey -keyalg EC -keystore keystore -groupname secp256r1 -alias localhost -keypass password -storepass password -dname "CN=localhost,OU=X,O=X,L=X,S=X,C=X"`
The conscypt jar has to be available, which can be retrieved with `wget https://repo1.maven.org/maven2/org/conscrypt/conscrypt-openjdk/2.4.0/conscrypt-openjdk-2.4.0-linux-x86_64.jar`
The code is compiled using `javac Main.java -cp conscrypt-openjdk-2.4.0-linux-x86_64.jar`
The code is run using `java -Xmx512M -cp conscrypt-openjdk-2.4.0-linux-x86_64.jar:. -Djavax.net.ssl.keyStore=keystore -Djavax.net.ssl.keyStorePassword=password -Djavax.net.ssl.trustStore=keystore -Djavax.net.ssl.trustStorePassword=password Main`
The code, to be placed inside Main.java:
````
import org.conscrypt.Conscrypt;
import org.conscrypt.OpenSSLProvider;
import java.io.*;
import javax.net.*;
import javax.net.ssl.*;
import java.net.*;
import java.security.Security;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
public static void main(String[] args) throws Exception {
System.out.println("pid: " + ProcessHandle.current().pid());
System.out.println("Use conscrypt? (y/n)");
boolean useConscrypt = new Scanner(System.in).nextLine().toLowerCase().startsWith("y");
if(useConscrypt) {
Conscrypt.setUseEngineSocketByDefault(true);
Security.insertProviderAt(new OpenSSLProvider(), 1);
}
int listenPort = 12345;
ServerSocketFactory serverSocketFactory = SSLServerSocketFactory.getDefault();
ServerSocket serverSocket = serverSocketFactory.createServerSocket(listenPort);
new Thread(()->{
try {
while(true) {
try(var socket = serverSocket.accept()) {
try(var out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())))) {
out.println("Hello");
out.flush();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
int concurrentThreads = Runtime.getRuntime().availableProcessors();
ExecutorService executor = Executors.newFixedThreadPool(concurrentThreads);
var count = new AtomicInteger(0);
var totalConnections = 100*1000;
new Thread(()->{
try {
while(true) {
Thread.sleep(1000);
System.out.println("Count: " + count.get());
}
}
catch (InterruptedException e) {}
}).start();
for(int i=0; i {
try {
SSLSocket socket = (SSLSocket) factory.createSocket("localhost", listenPort);
socket.startHandshake();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while ((in.readLine()) != null);
in.close();
socket.close();
count.incrementAndGet();
}
catch (IOException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
}
}
````
Thanks in advance for any assistance.
Contributor guide
Assessment
This issue has not been assessed yet.