2009-10-26 21 views
9

Tengo un componente de servidor que estoy tratando de cargar-prueba. Todas las conexiones al servidor usan TLS 1.0. Tengo un programa simple prueba que esencialmente hace esto en tantos hilos como yo quiero:La implementación de Java SSL de Sun está perdiendo memoria?

Full TLS handshake to the server 
send a request 
read reply 
close connection 
repeat ad nauseam 

Mi máquina virtual es el siguiente:

Java(TM) SE Runtime Environment (build 1.6.0_16-b01) 
Java HotSpot(TM) Server VM (build 14.2-b01, mixed mode) 

que tienen una pérdida de memoria. La huella de mi memoria aumenta en aproximadamente 1 meg por segundo cuando pongo a prueba mi servidor, lo que hace que bloquee después de 15-20 minutos con OutOfMemoryException.

Lo ejecuté en el generador de perfiles de Netbean y mostró que el aumento de la memoria era profundo dentro de la API de TLS.

¿Alguien ha experimentado algo similar? ¿Hay alguna solución alternativa que pueda implementar en mi nivel?

Editar. Conforme a lo solicitado, aquí está el rastreo de llamadas perfilado que genera una gran cantidad de estos byte []:

.java.io.ByteArrayOutputStream.<init>(int) 
..com.sun.net.ssl.internal.ssl.OutputRecord.<init>(byte, int) 
...com.sun.net.ssl.internal.ssl.OutputRecord.<init>(byte) 
....com.sun.net.ssl.internal.ssl.AppOutputStream.<init>(com.sun.net.ssl.internal.ssl.SSLSocketImpl) 
.....com.sun.net.ssl.internal.ssl.SSLSocketImpl.init(com.sun.net.ssl.internal.ssl.SSLContextImpl, boolean) 
......com.sun.net.ssl.internal.ssl.SSLSocketImpl.<init>(com.sun.net.ssl.internal.ssl.SSLContextImpl, java.net.Socket, String, int, boolean) 
.......com.sun.net.ssl.internal.ssl.SSLSocketFactoryImpl.createSocket(java.net.Socket, String, int, boolean) 
<my code> 

hay muchos más que puedo poner ... esto sería larga. Te voy a decir los puntos de entrada que el perfilador me da:

....com.sun.net.ssl.internal.ssl.AppOutputStream.<init>(com.sun.net.ssl.internal.ssl.SSLSocketImpl) 
....com.sun.net.ssl.internal.ssl.HandshakeOutStream.<init>(com.sun.net.ssl.internal.ssl.ProtocolVersion, com.sun.net.ssl.internal.ssl.ProtocolVersion, com.sun.net.ssl.internal.ssl.HandshakeHash, com.sun.net.ssl.internal.ssl.SSLSocketImpl) 
....com.sun.net.ssl.internal.ssl.SSLSocketImpl.sendAlert(byte, byte) 
..com.sun.net.ssl.internal.ssl.AppInputStream.<init>(com.sun.net.ssl.internal.ssl.SSLSocketImpl) 
..com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake() 
..com.sun.net.ssl.internal.ssl.HandshakeInStream.<init>(com.sun.net.ssl.internal.ssl.HandshakeHash) 
+2

¿Puede proporcionar algunos resultados de perfiles más específicos? La fuga no es necesariamente de TLS, podría estar en tu código. –

+1

Cree el programa más pequeño posible que muestre este comportamiento y agréguelo a su pregunta. –

Respuesta

4

¿Has visto la conexión cerrar. Lo más probable es que esto todavía esté abierto de alguna manera. 1Mb es una canción de algún hilo adicional. Sin embargo, no estoy seguro de cuál sería exactamente el motivo.

+0

Lo he visto. Estoy usando 'Executors.newScheduledThreadPool', por lo que no tengo el control total de la cantidad de subprocesos que se ejecutan en un momento dado. Dicho esto, los resultados del perfilador muestran que la mayor parte de la memoria se toma por byte []. – malaverdiere

+0

Verificaré que la desconexión del cliente se maneje correctamente y que la conexión se haya cerrado. Buena idea. – malaverdiere

+0

¡Sí, eso hizo una gran diferencia! Aunque se detectó la desconexión (atrapando la IOException), no estaba cerrando el SSLSocket ... – malaverdiere

8

Todas las conexiones SSL están asociadas con una sesión SSL, que puede ser reutilizado a través de conexiones TCP distintos para reducir la sobrecarga apretón de manos en la negociación de claves de cifrado temporales después de la se ha establecido una conexión TCP real. Puede ser que sus clientes estén de algún modo forzando la creación de una nueva sesión y dado que la configuración predeterminada para Java 6 parece almacenar en caché un número ilimitado de sesiones durante una hora, puede encontrarse fácilmente con un problema de memoria.

Puede manipular estas configuraciones para su socket de servidor obteniendo SSLSessionContext del socket del servidor con getSession(). GetSessionContext() y establezca el tamaño de caché con setSessionCacheSize y timeout (en segundos) con setSessionTimeout. Hubiera esperado que fuera posible cambiar la configuración predeterminada a través de las propiedades del sistema, pero no puedo encontrar ninguna documentación sobre eso. Tal vez puedas encontrar algo tú mismo buscando en Google un poco más de tiempo que yo.


¿Estás seguro de que estás estableciendo el límite en el contexto de sesión correcto? Estaba equivocado sobre el contexto accesible desde el socket del servidor. Usted tiene que fijar a través de la SSLContext antes de crear el socket de servidor:

SSLContext sslContext = SSLContext.getDefault(); 
sslContext.getServerSessionContext().setSessionCacheSize(1000); 
SSLServerSocket ss = (SSLServerSocket) 
    sslContext.getServerSocketFactory().createServerSocket(<port>); 

Sin esta limitación, era fácil de reproducir su memoria "fugas", ya que cada costuras de sesión SSL caché para que utilicen algún lugar alrededor de 7-800 bytes de la memoria del montón. Con el límite de recuento de sesiones, mi servidor se ha estado ejecutando bajo estrés durante aproximadamente 15 minutos y aún usa solo 3-4 MB de memoria de almacenamiento dinámico.

+0

Establecí un límite de 1000 sesiones. La curva antes del accidente no es tan empinada como antes (parece una escalera ahora), así que esto está ayudando. Sin embargo, todavía se cuelga. – malaverdiere

+0

¿Has probado el fragmento de código que sugerí en mi edición posterior? – jarnbjo

+2

86400sec es el tiempo de espera predeterminado (es decir, 24h) y puede establecer el tamaño de caché predeterminado: w/'-Djavax.net.ssl.sessionCacheSize = xxx' propiedad – bestsss

1

1 MB es la memoria necesaria para crear un hilo, adicional o no.

¿Hay alguna entrada en la lista de errores para esa clase o paquete? El primer paso sería verificarlo.

El segundo paso es suponer que el problema radica en su código, no en las cosas de Sun. Es más probable, simplemente porque usuarios de todo el mundo han golpeado a una clase de uso común en Java JDK. Si hubiera un error, ya habría salido a la luz.

Eso no quiere decir que el código JDK esté libre de errores, solo que primero debe sospechar de su código.

Obtenga un generador de perfiles y mida. No adivine

+1

+1 - sospeche primero su propio código –

+0

Como dije, me he ejecutado en un generador de perfiles. Toda la sobrecarga se debe a byte [] que se crea en lo profundo de la implementación de SSL. – malaverdiere

0

¿En qué hardware se está ejecutando? ¿Puedes hacer un netstat y verificar el estado de tus conexiones?

He probado la carga de Tomcat, y no he tenido problemas para lograr 500 nuevas solicitudes de SSL/seg, que se ejecutan durante horas, con un montón de 1 GB en Solaris. Además, es posible que desee controlar la cantidad de subprocesos que se ejecutan en el contenedor.

Cuestiones relacionadas