OpenVZ, JDK 7 I Problem Z Pamięcią

Podczas uruchamiania środowiska deweloperskiego w kontenerze OpenVZ natknąłem się na całkiem nietrywialny problem związany z alokacją pamięci dla JVM.

Specyfikacja kontenera jest następująca:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
root@z130:/opt# cat /proc/user_beancounters 
Version: 2.5
       uid  resource                     held              maxheld              barrier                limit              failcnt
      130:  kmemsize                 14478964             20152320            536870912            536870912                    0
            lockedpages                     0                    0                 2048                 2048                    0
            privvmpages                848352              2023920              1310720              1310720                  230
            shmpages                     1298                 3250               262144               262144                    0
            dummy                           0                    0  9223372036854775807  9223372036854775807                    0
            numproc                       191                  288                 2000                 2000                    0
            physpages                  563315               846889                    0  9223372036854775807                    0
            vmguarpages                     0                    0              1310720              1310720                    0
            oomguarpages               374320               405201                26112  9223372036854775807                    0
            numtcpsock                     26                   45                  360                  360                    0
            numflock                       27                   30                  188                  206                    0
            numpty                          5                    9                  100                  100                    0
            numsiginfo                      0                   12                  256                  256                    0
            tcpsndbuf                  213344               395208              1720320              2703360                    0
            tcprcvbuf                  214776              1119568              1720320              2703360                    0
            othersockbuf                 2312                54072              1126080              2097152                    0
            dgramrcvbuf                     0                 9248               262144               262144                    0
            numothersock                   40                   52                  360                  360                    0
            dcachesize                3498040              3624960              3409920              3624960                    0
            numfile                      1320                 1805                 9312                 9312                    0
            dummy                           0                    0  9223372036854775807  9223372036854775807                    0
            dummy                           0                    0  9223372036854775807  9223372036854775807                    0
            dummy                           0                    0  9223372036854775807  9223372036854775807                    0
            numiptent                      24                   24                 1000                 1000                    0

Co daje około 5GB wirtualnego RAMu

1
2
3
4
5
root@z130:/opt# free
             total       used       free     shared    buffers     cached
Mem:       5242880    3393488    1849392          0          0     741796
-/+ buffers/cache:    2651692    2591188
Swap:      1048576          0    1048576

Na maszynie uruchomione są 3 x JVM 7 64-bit (jdk-7u25-linux-x64), które sumarycznie zużywają około 3GB. Podczas uruchamiania kolejnej JVM (-Xmx512MB) pojawił się następujący problem

1
2
3
4
Error occurred during initialization of VM
Could not reserve enough space for object heap
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

Mimo, że pamięci na heap nie brakuje, JVM zgłasza błąd. W czym zatem leży problem? Po dłuższej analizie i wskazówkach otrzymanych po przeczytaniu blogu Mathieu Hicaubera okazało się, że problem leży w ciągłych blokach pamięci, które potrzebuje JVM w trybie server a których OpenVZ (lub system, fix me) nie jest w stanie dostarczyć. Krótko mówiąc JVM w trybie server próbuje zaalokować całą potrzebną pamięć, co niestety się jej nie udaje.

Rozwiązanie

Znalazłem dwa rozwiązania, które pomogą w takich przypadkach:

Przełączenie JVM w tryb client

Na pierwszy rzut oka idealnym rozwiązaniem jest w takim przypadku przełączenie JVM w tryb client, który alokuje pamięć on the fly a nie podczas startu. Tutaj pojawia się jednak niespodzianka - z JRE 7 64-bit ten tryb został usunięty ;) Co zatem można zrobić w takiej sytuacji? Gdy napotkasz powyższy problem, a nie potrzebujesz więcej pamięci na stertę niż 2GB, możesz przełączyć się na JVM 32-bit i skorzystać z trybu client … Nie jest to jednak takie trywialne zadanie jak mogłoby się wydawać ;) Gdy posiadasz maszynę, która mają więcej niż 2GB pamięci przełącznik “-client” po prostu nie zadziała. Dzieje się tak z powodu konfiguracji JVM zapisanej w pliku jvm.cfg

1
2
3
4
5
6
7
root@z130:/opt/jdk-7u25-linux-i586/jre/lib/i386# cat jvm.cfg 
-client IF_SERVER_CLASS -server
-server KNOWN
-hotspot ALIASED_TO -client
-classic WARN
-native ERROR
-green ERROR

Dokładnie chodzi o linijkę “-client IF_SERVER_CLASS -server”

1
Note: For Java SE 6, the definition of a server-class machine is one with at least 2 CPUs and at least 2GB of physical memory.

W takim przypadku wystarczy mały hack w postaci dostosowania konfiguracji do naszych potrzeb. Należy zmienić zawartość pliku jvm.cfg na

1
2
3
4
5
6
7
#-client IF_SERVER_CLASS -server
-client KNOWN
-server KNOWN
-hotspot ALIASED_TO -client
-classic WARN
-native ERROR
-green ERROR

A co w przypadkach, gdy potrzebujemy więcej niż 2GB sterty? Jeżeli masz taką możliwość, możesz skorzystać z poniższego rozwiązania.

Zwiększenie PRIVVMPAGES w konfiguracji kontenera OpenVZ

Jest to opcja, z której skorzystałem i polega na lekkim zwiększeniu wartości parametru PRIVVMPAGES kontenera. Parametr ten oznacza maksymalny rozmiar pamięci, który może zostać przydzielony procesom. Maksymalny rozmiar pamięci, który może wykorzystać kontener jest z kolei określany przez parametr VMGUARPAGES. Otrzymamy zatem efekt zwiększenia “zakresu poszukiwań” ciągłych bloków posiadając wciąż ograniczenie na maksymalne zużycie fizycznej pamięci przez kontener. Aby zwiększyć ten parametr wykonujemy polecenie

1
2
3
4
5
6
[root@n7.dc1 ~]# vzctl set 130 --privvmpages $((256 * 8 * 1024)) --save
[root@z130]:/opt# free
             total       used       free     shared    buffers     cached
Mem:       7340032    3286472    4053560          0          0     745464
-/+ buffers/cache:    2541008    4799024
Swap:      1048576          0    1048576

Należy być jednak ostrożnym podczas pracy z takim kontenerem ponieważ logicznie dostępnych będzie 8GB pamięci a fizycznie gwarantujemy tylko 5GB. Gdy któryś z procesów będzie próbował zaalokować pamięć i przekroczy przy tym limit dla VMGUARPAGES zostanie po prostu skillowany …

Comments