문제

Linux에서 실행되는 Java 응용 프로그램에 문제가 있습니다.

기본 최대 힙 크기 (64MB)를 사용하여 애플리케이션을 시작할 때 240MB의 가상 메모리가 응용 프로그램에 할당 된 상단 응용 프로그램을 사용합니다. 이는 컴퓨터의 다른 소프트웨어와 관련하여 일부 문제가 발생하며, 이는 비교적 리소스 제한입니다.

예약 된 가상 메모리는 어쨌든 내가 이해하는 한, 힙 제한에 도달하면 OutOfMemoryError 던져졌다. Windows에서 동일한 응용 프로그램을 실행했으며 가상 메모리 크기와 힙 크기가 비슷하다는 것을 알 수 있습니다.

어쨌든 Linux에서 Java 프로세스에 사용되는 가상 메모리를 구성 할 수 있습니까?

편집 1: 문제는 힙이 아닙니다. 문제는 예를 들어 128MB의 힙을 설정하면 여전히 Linux가 210MB의 가상 메모리를 할당한다는 것입니다.

편집 2: 사용 ulimit -v 가상 메모리의 양을 제한 할 수 있습니다. 크기 세트가 204MB 미만인 경우 204MB가 필요하지 않더라도 응용 프로그램은 64MB 만 실행되지 않습니다. 따라서 Java가 왜 그렇게 많은 가상 메모리를 요구하는지 이해하고 싶습니다. 이것이 바뀔 수 있습니까?

편집 3: 시스템에서 실행되는 몇 가지 다른 응용 프로그램이 포함되어 있습니다. 그리고 시스템에는 가상 메모리 제한이 있습니다 (주석, 중요한 세부 사항).

도움이 되었습니까?

해결책

이것은 Java에 대한 오랜 불만 이었지만 대부분 의미가 없으며 일반적으로 잘못된 정보를 바라 보는 것을 기반으로합니다. 일반적인 문구는 "Java의 Hello World on Java는 10 메가 바이트를 사용합니다! 왜 필요한가요?" 글쎄, 여기에 64 비트 JVM 주장에서 Hello World를 4 기가 바이트를 점령하겠다고 주장하는 방법이 있습니다. 적어도 한 가지 형태의 측정으로.

java -Xms1024m -Xmx4096m com.example.Hello

메모리를 측정하는 다른 방법

Linux에서 맨 위 명령은 메모리에 대해 여러 가지 숫자를 제공합니다. Hello World 예제에 대한 내용은 다음과 같습니다.

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 2120 kgregory  20   0 4373m  15m 7152 S    0  0.2   0:00.10 java
  • 미덕은 가상 메모리 공간입니다. 가상 메모리 맵의 모든 것 (아래 참조). 그렇지 않은 경우를 제외하고는 거의 의미가 없습니다 (아래 참조).
  • RES는 거주 세트 크기입니다. 현재 RAM에 상주하는 페이지 수입니다. 거의 모든 경우에, 이것은 "너무 큰"이라고 말할 때 사용해야 할 유일한 숫자입니다. 그러나 특히 Java에 대해 이야기 할 때는 여전히 좋은 숫자가 아닙니다.
  • SHR은 다른 프로세스와 공유되는 상주 메모리의 양입니다. Java 프로세스의 경우 일반적으로 공유 라이브러리 및 메모리 매핑 된 Jarfiles로 제한됩니다. 이 예에서는 하나의 Java 프로세스 만 실행 했으므로 7K는 OS에서 사용하는 라이브러리의 결과라고 생각합니다.
  • 스왑은 기본적으로 켜지지 않으며 여기에 표시되지 않습니다. 현재 디스크에 상주하는 가상 메모리의 양을 나타냅니다. 실제로 스왑 공간에 있는지 여부. OS는 활성 페이지를 RAM으로 유지하는 데 매우 좋습니다. 교환의 유일한 치료법은 (1) 더 많은 메모리를 구매하거나 (2) 프로세스 수를 줄이 므로이 숫자를 무시하는 것이 가장 좋습니다.

Windows Task Manager의 상황은 조금 더 복잡합니다. Windows XP에는 "메모리 사용"및 "가상 메모리 크기"열이 있지만 공식 문서 그들이 의미하는 바에 침묵합니다. Windows Vista 및 Windows 7 더 많은 열을 추가하면 실제로 문서화. 이 중 "작업 세트"측정이 가장 유용합니다. Linux에서 RES 및 SHR의 합에 대략적으로 일치합니다.

가상 메모리 맵 이해

프로세스에 의해 소비되는 가상 메모리는 프로세스 메모리 맵에있는 모든 것의 총계입니다. 여기에는 데이터 (예 : Java Heap)뿐만 아니라 프로그램에서 사용하는 모든 공유 라이브러리 및 메모리 매핑 파일도 포함됩니다. Linux에서는 사용할 수 있습니다 오후 공정 공간에 매핑 된 모든 것들을 보도록 명령합니다 (여기서부터 나는 Linux를 참조 할 것입니다. 그것이 내가 사용하는 것이기 때문에, Windows와 동등한 도구가 있다고 확신합니다). 다음은 "Hello World"프로그램의 메모리 맵에서 발췌 한 내용입니다. 전체 메모리 맵의 길이는 100 줄 이상이며 천선 목록을 갖는 것은 드문 일이 아닙니다.

0000000040000000     36K r-x--  /usr/local/java/jdk-1.6-x64/bin/java
0000000040108000      8K rwx--  /usr/local/java/jdk-1.6-x64/bin/java
0000000040eba000    676K rwx--    [ anon ]
00000006fae00000  21248K rwx--    [ anon ]
00000006fc2c0000  62720K rwx--    [ anon ]
0000000700000000 699072K rwx--    [ anon ]
000000072aab0000 2097152K rwx--    [ anon ]
00000007aaab0000 349504K rwx--    [ anon ]
00000007c0000000 1048576K rwx--    [ anon ]
...
00007fa1ed00d000   1652K r-xs-  /usr/local/java/jdk-1.6-x64/jre/lib/rt.jar
...
00007fa1ed1d3000   1024K rwx--    [ anon ]
00007fa1ed2d3000      4K -----    [ anon ]
00007fa1ed2d4000   1024K rwx--    [ anon ]
00007fa1ed3d4000      4K -----    [ anon ]
...
00007fa1f20d3000    164K r-x--  /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
00007fa1f20fc000   1020K -----  /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
00007fa1f21fb000     28K rwx--  /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
...
00007fa1f34aa000   1576K r-x--  /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3634000   2044K -----  /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3833000     16K r-x--  /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3837000      4K rwx--  /lib/x86_64-linux-gnu/libc-2.13.so
...

형식에 대한 빠른 설명 : 각 행은 세그먼트의 가상 메모리 주소로 시작합니다. 다음으로 세그먼트 크기, 권한 및 세그먼트 소스가 이어집니다. 이 마지막 항목은 파일 또는 "anon"이며,이를 통해 할당 된 메모리 블록을 나타냅니다. MMAP.

정상에서 시작하여 우리는 가지고 있습니다

  • JVM 로더 (즉, 입력 할 때 실행되는 프로그램 java). 이것은 매우 작습니다. 실제 JVM 코드가 저장된 공유 라이브러리에로드됩니다.
  • 자바 힙과 내부 데이터를 고정하는 많은 Anon 블록. 이것은 Sun JVM이므로 힙은 여러 세대로 나뉘며 각각은 자체 메모리 블록입니다. JVM은 가상 메모리 공간을 기준으로 할당합니다. -Xmx 값; 이것은 인접한 힙을 가질 수있게합니다. 그만큼 -Xms 값은 프로그램이 시작될 때 "사용중인"힙의 양을 말하고 해당 한계에 접근 할 때 쓰레기 수집을 트리거하기 위해 내부적으로 사용됩니다.
  • 메모리 매핑 된 jarfile,이 경우 "JDK 클래스"를 보유하는 파일. 항아리를 메모리 매핑하면 내부의 파일에 매우 효율적으로 액세스 할 수 있습니다 (매번 처음부터 읽는 것과 비슷합니다). Sun JVM은 클래스 경로에 모든 항아리를 메모리 매핑합니다. 애플리케이션 코드가 항아리에 액세스 해야하는 경우 메모리를 메모리 할 수도 있습니다.
  • 두 스레드에 대한 스레드 당 데이터. 1m 블록은 스레드 스택입니다. 나는 4K 블록에 무엇이 들어가는 지 모른다. 실제 앱의 경우 메모리 맵을 통해 수백 개의 항목이 반복되지 않으면 수십 개의 톤이 표시됩니다.
  • 실제 JVM 코드를 보유하는 공유 라이브러리 중 하나입니다. 이것들 중 몇 가지가 있습니다.
  • C 표준 라이브러리의 공유 라이브러리. 이것은 JVM이 Java의 일부가 아닌 많은 것들 중 하나 일뿐입니다.

공유 라이브러리는 특히 흥미 롭습니다. 각 공유 라이브러리에는 라이브러리 코드가 포함 된 읽기 전용 세그먼트와 라이브러리에 대한 글로벌 프로세스 당 데이터가 포함 된 읽기 쓰기 세그먼트입니다. 권한이없는 세그먼트는 다음과 같습니다. x64 linux에서만 보았습니다). 라이브러리의 읽기 전용 부분은 라이브러리를 사용하는 모든 프로세스간에 공유 할 수 있습니다. 예를 들어, libc 공유 할 수있는 1.5m의 가상 메모리 공간이 있습니다.

가상 메모리 크기는 언제 중요합니까?

가상 메모리 맵에는 많은 것들이 포함되어 있습니다. 그것 중 일부는 읽기 전용이며 일부는 공유되며 일부는 공유되며 일부는 할당되었지만 결코 만지지 않았습니다 (예 : 거의 모든 4GB 힙은이 예에서). 그러나 운영 체제는 필요한 것을로드 할 수있을 정도로 똑똑하므로 가상 메모리 크기는 크게 관련이 없습니다.

가상 메모리 크기가 중요한 경우 32 비트 운영 체제에서 실행중인 경우 2GB (또는 경우에 따라 3GB)의 프로세스 주소 공간 만 할당 할 수 있습니다. 이 경우 부족한 자원을 다루고 있으며 큰 파일을 메모리 매핑하거나 많은 스레드를 생성하기 위해 힙 크기를 줄이는 등 트레이드 오프를해야 할 수도 있습니다.

그러나 64 비트 기계가 어디에나 있기 때문에 가상 메모리 크기가 완전히 관련이없는 통계가되기 오래 걸리지 않을 것이라고 생각합니다.

거주자 세트 크기는 언제 중요합니까?

상주 세트 크기는 실제로 RAM에있는 가상 메모리 공간의 일부입니다. RSS가 총 물리적 기억의 상당 부분으로 성장하면 걱정을 시작해야 할 때가 될 수 있습니다. RSS가 모든 물리적 메모리를 차지하기 위해 성장하고 시스템이 바뀌기 시작하면 걱정하기 시작하는 시간이 지났습니다.

그러나 RSS는 특히 가볍게로드 된 기계에서 오해의 소지가 있습니다. 운영 체제는 프로세스에서 사용하는 페이지를 회수하기 위해 많은 노력을 기울이지 않습니다. 그렇게함으로써 얻을 수있는 이점은 거의 없으며, 프로세스가 향후 페이지에 닿으면 값 비싼 페이지 결함의 가능성이 없습니다. 결과적으로 RSS 통계에는 활성화되지 않은 많은 페이지가 포함될 수 있습니다.

결론

교환하지 않는 한 다양한 메모리 통계가 말하는 것에 대해 지나치게 걱정하지 마십시오. 끊임없이 성장하는 RSS가 어떤 종류의 메모리 누출을 나타낼 수 있다는 경고를 사용합니다.

Java 프로그램을 사용하면 힙에서 일어나는 일에주의를 기울이는 것이 훨씬 더 중요합니다. 소비되는 총 공간의 양이 중요하며,이를 줄이기 위해 취할 수있는 몇 가지 단계가 있습니다. 더 중요한 것은 쓰레기 수집에 소비하는 시간과 힙의 일부를 수집하는 것입니다.

디스크에 액세스하는 것은 비싸고 메모리는 저렴합니다. 하나를 상대적으로 거래 할 수 있다면 그렇게하십시오.

다른 팁

Java 및 Glibc> = 2.10에는 알려진 문제가 있습니다 (Ubuntu> = 10.04, Rhel> = 6 포함).

치료법은이 부동산을 설정하는 것입니다. 변하기 쉬운:

export MALLOC_ARENA_MAX=4

Tomcat을 실행중인 경우 다음에 추가 할 수 있습니다. TOMCAT_HOME/bin/setenv.sh 파일.

Docker의 경우 이것을 Dockerfile에 추가하십시오

ENV MALLOC_ARENA_MAX=4

malloc_arena_max 설정에 관한 IBM 기사가 있습니다https://www.ibm.com/developerworks/community/blogs/kevgrig/entry/linux_glibc_2_10_rhel_6_malloc_may_show_excipice_virtual_memory_usage?lang=en

이 블로그 게시물이 말합니다

레지던트 메모리는 메모리 누출 또는 메모리 조각화와 유사한 방식으로 기어 오르는 것으로 알려져 있습니다.

열린 JDK 버그도 있습니다 JDK-8193521 "Glibc는 기본 구성으로 메모리를 낭비합니다"

더 많은 참조를 보려면 Google에서 malloc_arena_max를 검색하십시오.

할당 된 메모리의 낮은 조각화를 최적화하기 위해 다른 malloc 옵션도 조정할 수 있습니다.

# tune glibc memory allocation, optimize for low fragmentation
# limit the number of arenas
export MALLOC_ARENA_MAX=2
# disable dynamic mmap threshold, see M_MMAP_THRESHOLD in "man mallopt"
export MALLOC_MMAP_THRESHOLD_=131072
export MALLOC_TRIM_THRESHOLD_=131072
export MALLOC_TOP_PAD_=131072
export MALLOC_MMAP_MAX_=65536

Java 프로세스에 할당 된 메모리의 양은 내가 기대할 수있는 것과 거의 관련이 있습니다. 임베디드/메모리 제한 시스템에서 Java를 실행하는 비슷한 문제가있었습니다. 달리기 어느 임의의 VM 한도 또는 적절한 양의 스왑이없는 시스템에 대한 응용 프로그램은 파괴되는 경향이 있습니다. 리소스 제한 시스템에서 사용하기 위해 설계되지 않은 많은 최신 앱의 특성 인 것 같습니다.

JVM의 메모리 발자국을 시도하고 제한 할 수있는 몇 가지 옵션이 더 있습니다. 이렇게하면 가상 메모리 발자국이 줄어 듭니다.

-XX : ReservedCodeCachesize = 32m 예약 코드 캐시 크기 (바이트) - 최대 코드 캐시 크기. [Solaris 64 비트, AMD64 및 -server x86 : 48m; 1.5.0_06 이상에서 Solaris 64 비트 및 64 : 1024m.

-XX : MaxPermsize = 영구 생성의 64m 크기. [5.0 및 최신 : 64 비트 VM은 30% 더 크게 확장됩니다. 1.4 AMD64 : 96m; 1.3.1 -Client : 32m.

또한 -xmx (최대 힙 크기)를 가능한 한 가까운 값으로 설정해야합니다. 실제 피크 메모리 사용 귀하의 신청서. 나는 JVM의 기본 동작이 여전히 더블 힙 크기는 매번 최대로 확장 할 때마다. 32m 힙으로 시작하고 앱이 65m로 정점에 도달하면 힙이 32m-> 64m-> 128m로 늘어납니다.

힙 성장에 대해 VM을 덜 공격적으로 만들기 위해 이것을 시도 할 수도 있습니다.

-XX : MinHeapFreeratio = 40 GC 후 확장을 피하기 위해 힙이없는 최소 백분율.

또한 몇 년 전이 실험을 통해 기억 한 바에 따르면,로드 된 기본 라이브러리의 수는 최소 발자국에 큰 영향을 미쳤습니다. java.net.socket을로드하면 올바르게 기억하면 15m 이상이 추가되었습니다 (그리고 아마도 그렇지 않을 것입니다).

Sun JVM은 핫스팟에 대한 많은 메모리가 필요하며 공유 메모리의 런타임 라이브러리에 맵핑됩니다.

메모리가 문제 인 경우 임베딩에 적합한 다른 JVM을 사용하는 것을 고려하십시오. IBM에는 J9가 있으며 GNU ClassPath 라이브러리를 사용하는 오픈 소스 "JAMVM"이 있습니다. 또한 Sun은 Squeak JVM이 태양 흑점에서 실행되므로 대안이 있습니다.

단지 생각이지만, 당신은의 영향을 확인할 수 있습니다. ulimit -v 옵션.

그것은 가능한 주소 공간을 제한하기 때문에 실제 솔루션이 아닙니다. 모두 프로세스이지만 제한된 가상 메모리로 응용 프로그램의 동작을 확인할 수 있습니다.

자원이 제한된 시스템의 힙을 줄이는 한 가지 방법은 -XX : MaxHeapFreeratio 변수와 함께 놀 수있는 것일 수 있습니다. 이것은 일반적으로 70으로 설정되며 GC가 축소되기 전에 무료 인 힙의 최대 백분율입니다. 더 낮은 값으로 설정하면 예를 들어 jvisualvm 프로파일 러에서는 더 작은 힙 SIC가 일반적으로 프로그램에 사용된다는 것을 알 수 있습니다.

편집 : -xx에 대한 작은 값을 설정하려면 MaxHeapFreeratio도 -XX를 설정해야합니다 : MinHeapFreeratio EG

java -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=25 HelloWorld

edit2 : 기본 매개 변수가 있고 하나는 10과 25가 매개 변수로 시작하는 실제 응용 프로그램에 대한 예를 추가했습니다. 이론적으로 Java는 후자의 예에서 힙을 늘리기 위해 더 많은 시간을 사용해야하지만 실제 속도 차이는 눈치 채지 못했습니다.

Default parameters

결국, Max Heep는 905, 중고 힙은 378입니다.

MinHeap 10, MaxHeap 25

결국, Max Heep은 722, 중고 힙은 378입니다.

응용 프로그램이 원격 데스크탑 서버에서 실행되므로 많은 사용자가 한 번에 실행할 수 있으므로 실제로는 중요합니다.

Sun 's Java 1.4는 메모리 크기를 제어하기위한 다음과 같은 주장이 있습니다.

-XMSN 메모리 할당 풀의 초기 크기를 바이트로 지정합니다. 이 값은 1MB보다 큰 1024의 배수 여야합니다. 킬로 바이트를 나타내려면 문자 k 또는 k를 추가하여 메가 바이트를 나타 내기 위해 m 또는 m을 나타냅니다. 기본값은 2MB입니다. 예 :

           -Xms6291456
           -Xms6144k
           -Xms6m

-xmxn 메모리 할당 풀의 최대 크기 (바이트)를 지정합니다. 이 값은 1024의 배수가 2MB보다 커야합니다. 킬로 바이트를 나타내려면 문자 k 또는 k를 추가하여 메가 바이트를 나타 내기 위해 m 또는 m을 나타냅니다. 기본값은 64MB입니다. 예 :

           -Xmx83886080
           -Xmx81920k
           -Xmx80m

http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/java.html

Java 5와 6에는 더 많은 것이 있습니다. 보다 http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp

아니요, VM에 필요한 메모리 양을 구성 할 수 없습니다. 그러나 이것은 상주가 아닌 가상 메모리이므로 실제로 사용되지 않으면 해를 입지 않고 유지됩니다.

놀랍게도, 당신은 다른 JVM을 시도한 다음 Sun One을 시도 할 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top