2009-10-26 9 views
9

Mam składnik serwera, który próbuję załadować test. Wszystkie połączenia z serwerem korzystają z TLS 1.0. Mam prosty program testowy, który w zasadzie robi to na tyle wątków, jak chcę:Wdrożenie Java SSL przez Sun to przeciekająca pamięć?

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

Moja maszyna wirtualna jest następujący:

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

Mam wyciek pamięci. Mój ślad pamięci zwiększa się o około 1 megawata na sekundę, kiedy mocno testuję mój serwer, co sprawia, że ​​blokuje się po 15-20 minutach z OutOfMemoryException.

Uruchomiłem go w narzędziu profilującym Netbean i pokazałem, że zwiększenie pamięci było głęboko w interfejsie API TLS.

Czy ktoś kiedykolwiek doświadczył czegoś podobnego? Czy istnieje jakieś obejście, które mogę wdrożyć na moim poziomie?

Edytuj. Zgodnie z wnioskiem, oto profilowanie ślad wezwanie, które generuje dużo tych 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> 

Istnieje wiele więcej mogę umieścić ... to będzie długa. Powiem wam, że punkty wejścia profilera daje mi:

....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

Czy możesz podać bardziej szczegółowe wyniki profilowania? Wyciek niekoniecznie musi pochodzić z TLS, może być w twoim kodzie. –

+1

Należy utworzyć najmniejszy możliwy program, który wykazuje to zachowanie i dodać go do pytania. –

Odpowiedz

4

Widziałeś połączenie w pobliżu. Najprawdopodobniej jest to wciąż jakoś otwarte. 1Mb to śpiewa o jakimś dodatkowym wątku. Jednak nie jestem pewien, jaki byłby to powód.

+0

Sprawdziłem to. Używam 'Executors.newScheduledThreadPool', więc nie mam pełnej kontroli nad tym, ile wątków jest uruchomionych w danym momencie. Biorąc to pod uwagę, wyniki profilera pokazują, że większość pamięci jest pobierana przez bajt []. – malaverdiere

+0

Dwa razy sprawdzę, czy rozłączenie klienta jest obsługiwane poprawnie, a połączenie jest zamknięte. Dobry pomysł. – malaverdiere

+0

Tak, to zrobiło wielką różnicę! Mimo że wykryto odłączenie (przez uchwycenie wyjątku IOException), nie zamykałem SSLSocket ... – malaverdiere

8

Wszystkie połączenia SSL są związane z sesji SSL, które mogą być ponownie wykorzystane w poprzek różnych połączeń TCP, aby zmniejszyć obciążenie handshake podczas negocjacji tymczasowe klucze szyfrowania po faktyczne połączenie TCP zostało ustanowione. Możliwe, że twoi klienci w jakiś sposób zmuszają do utworzenia nowej sesji, a ponieważ domyślna konfiguracja Java 6 wydaje się buforować nieograniczoną liczbę sesji przez jedną godzinę, możesz łatwo napotkać problem z pamięcią.

Można modyfikować te ustawienia dla gniazda serwera, pobierając obiekt SSLSessionContext z gniazda serwera za pomocą metody getSession(). GetSessionContext() i ustawiając rozmiar pamięci podręcznej za pomocą metody setSessionCacheSize i limitu czasu (w sekundach) za pomocą metody setSessionTimeout. Spodziewałbym się, że będzie możliwa zmiana domyślnej konfiguracji poprzez właściwości systemu, ale nie jestem w stanie znaleźć żadnej dokumentacji na ten temat. Być może znajdziesz coś samemu, szukając go nieco dłużej niż ja.


Czy jesteś pewien, że ustawiasz limit właściwego kontekstu sesji? Myliłem się co do kontekstu dostępnego z gniazda serwera. Trzeba ustawić go przez SSLContext przed utworzeniem gniazda serwera:

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

Bez tego ograniczenia, to było łatwe do odtworzenia swoją pamięć „przeciek”, ponieważ każdy buforowane szwy sesji SSL użyć gdzieś około 7-800 bajtów pamięci sterty. Po przekroczeniu limitu sesji mój serwer działa już od około 15 minut i nadal używa tylko 3-4 MB pamięci sterty.

+0

Ustawiłem limit 1000 sesji. Krzywa przed katastrofą nie jest tak stroma jak poprzednio (wygląda teraz jak schody), więc to pomaga. Mimo wszystko nadal się zawiesza. – malaverdiere

+0

Czy wypróbowałeś fragment kodu sugerowany w mojej edycji postu? – jarnbjo

+2

86400sec to domyślny limit czasu (tj. 24h) i można ustawić domyślny rozmiar pamięci podręcznej: w/'-Djavax.net.ssl.sessionCacheSize = xxx' property – bestsss

1

1 MB to pamięć wymagana do utworzenia wątku, dodatkowego lub nie.

Czy są jakieś wpisy na liście błędów tej klasy lub pakietu? Pierwszym krokiem byłoby sprawdzenie go.

Drugi krok polega na założeniu, że problem leży w kodzie, a nie w produktach Sun. Jest bardziej prawdopodobne, po prostu dlatego, że powszechnie używana klasa w JDK Java została uderzona przez użytkowników na całym świecie. Gdyby wystąpił błąd, już by się wyszło.

To nie znaczy, że kod JDK jest wolny od błędów, tylko że powinieneś najpierw podejrzewać swój kod.

Uzyskaj profilera i środek. Nie zgaduj.

+1

+1 - podejmij najpierw swój kod –

+0

Jak już powiedziałem, uruchomiłem profilera. Cały nadmiar wynika z bajtu [], które są tworzone głęboko w implementacji SSL. – malaverdiere

0

Z jakiego sprzętu korzystasz? Czy możesz zrobić netstat i zweryfikować stan twoich połączeń?

Mam przetestowany Tomcat i nie miałem problemów z uzyskaniem 500 nowych żądań SSL/s, działających przez wiele godzin, z 1 gigabajtową stertą w systemie Solaris. Możesz również monitorować liczbę wątków uruchomionych w kontenerze.

Powiązane problemy