Linux에서 Java의 가상 메모리 사용량, 너무 많은 메모리가 사용됨
Linux에서 실행되는 Java 응용 프로그램에 문제가 있습니다.
애플리케이션을 기동하면, 디폴트의 최대 히프 사이즈(64 MB)를 사용해 애플리케이션에 240 MB의 가상 메모리가 할당되어 있는 것을 알 수 있습니다.이로 인해 시스템의 일부 다른 소프트웨어에서 리소스가 상대적으로 제한된 문제가 발생합니다.
예약된 가상 메모리는 어차피 사용되지 않을 것입니다. 왜냐하면 힙 제한에 도달하면OutOfMemoryError
던집니다.윈도우에서 동일한 애플리케이션을 실행했는데 가상 메모리 크기와 힙 크기가 비슷합니다.
Linux에서 Java 프로세스에 사용 중인 가상 메모리를 구성할 수 있는 방법이 있습니까?
Edit 1: 문제는 히프가 아닙니다.문제는 예를 들어 힙을 128MB로 설정해도 Linux는 210MB의 가상 메모리를 할당할 수 있다는 것입니다.이것은 불필요합니다.**
편집 2: 사용ulimit -v
그럼 가상 메모리의 양을 제한할 수 있습니다.설정된 크기가 204MB 미만일 경우 204MB가 필요 없고 64MB만 있어도 응용 프로그램이 실행되지 않기 때문에 Java가 왜 이렇게 많은 가상 메모리를 필요로 하는지 알고 싶습니다.이거 바꿀 수 있어요?
Edit 3(편집 3): 시스템에 내장된 다른 여러 응용 프로그램이 실행되고 있습니다.또한 시스템에는 가상 메모리 제한이 있습니다(댓글, 중요한 세부 정보).
이것은 Java에 대한 오랜 불만이었지만, 대체로 의미가 없고, 잘못된 정보를 보는 것에 근거하고 있습니다.일반적인 표현은 "Hello World on Java는 10메가바이트를 가져간다!그게 왜 필요한 거죠?"64비트 JVM에서 Hello World를 4기가바이트 이상 차지한다고 주장하는 방법은 다음과 같습니다. 적어도 한 가지 방법으로 측정해야 합니다.
java - Xms1024m - Xmx4096m com. 예.안녕
다양한 메모리 측정 방법
Linux 에서는, top 커맨드를 사용하면, 메모리의 몇개의 다른 번호가 표시됩니다.Hello World의 예에 대해 다음과 같이 기술되어 있습니다.
PID 사용자 PR NI VIRT RES S %CPU %MEM TIME + 명령어2120 kgregory 20 0 4373 m 15 m 7152 S 0 . 2 0 . 00 . 10 java
- VIRT는 가상 메모리 공간입니다.가상 메모리 맵의 모든 합계를 나타냅니다(아래 참조).그렇지 않은 경우를 제외하고는 대부분 의미가 없습니다(아래 참조).
- RES는 상주 세트 크기입니다. RAM에 현재 상주하는 페이지 수입니다.거의 모든 경우, "너무 크다"고 말할 때 이 숫자만 사용해야 합니다.그러나 이는 특히 자바에 대해 말할 때 여전히 좋은 수치는 아니다.
- SHR은 다른 프로세스와 공유되는 상주 메모리의 양입니다.Java 프로세스의 경우 일반적으로 공유 라이브러리와 메모리에 매핑된 JAR 파일로 제한됩니다.이 예에서는 Java 프로세스가 1개밖에 실행되고 있지 않기 때문에 7k는 OS에서 사용되는 라이브러리의 결과라고 생각됩니다.
- SWAP는 기본적으로 활성화되어 있지 않으며 여기에 표시되어 있지 않습니다.실제로 스왑 공간에 있는지 여부에 관계없이 현재 디스크에 상주하는 가상 메모리의 양을 나타냅니다.OS는 활성 페이지를 RAM에 저장하는 데 매우 적합합니다.스왑의 유일한 방법은 (1)메모리를 증설하거나 (2)프로세스의 수를 줄이는 것입니다.따라서 이 숫자는 무시하는 것이 가장 좋습니다.
Windows 태스크 매니저의 상황은 조금 더 복잡합니다.Windows XP 에서는, 「메모리 사용량」과 「가상 메모리 사이즈」의 열이 있습니다만, 공식 메뉴얼에서는 그 의미에 대해서는 언급하지 않습니다.Windows Vista 및 Windows 7 에서는 열이 추가되어 실제로 문서화되어 있습니다.이 중 "작업 세트" 측정이 가장 유용합니다. Linux의 RES와 SHR의 합계와 거의 일치합니다.
가상 메모리 맵의 개요
프로세스가 소비하는 가상 메모리는 프로세스 메모리 맵에 있는 모든 메모리의 합계입니다.여기에는 데이터(예: Java 힙)뿐만 아니라 프로그램에서 사용하는 모든 공유 라이브러리와 메모리 매핑 파일도 포함됩니다.Linux 에서는 pmap 명령어를 사용하여 프로세스 공간에 매핑된 모든 것을 볼 수 있습니다(여기서부터는 Linux만을 참조합니다.사용하는 것은 Linux뿐입니다.Windows용 툴도 있을 것입니다).여기 "Hello World" 프로그램의 메모리 맵에서 발췌한 것이 있습니다. 전체 메모리 맵은 100줄이 넘습니다. 그리고 1,000줄 목록이 있는 것은 드문 일이 아닙니다.
0000004000036K r-x--/usr/local/java/jdk-1.6-x64/bin/java00000040108000 8K rwx--/usr/local/java/jdk-1.6-x64/bin/java00000040eba000676K rwx--[ anon ]00000006fae00000 21248K rwx-- [ anon ]00000006fc2c000062720K rwx--[ anon ]000000070000699072K rwx - [ anon ]000000072aab0000 2097152K rwx-- [ anon ]00000007aaab00349504K rwx--[ anon ]00000007c00000001048576K 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.그렇게00007fa1f20fc000 1020K ----- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.그렇게00007fa1f21fb000 28K rwx--/usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.그렇게...00007fa1f34a000 1576K r-x--/lib/x86_64-linux-buff/libc-2.13.so00007fa1f3634000 2044K ----- /lib/x86_64-linux-buff/libc-2.13.so00007fa1f3833000 16K r-x--/lib/x86_64-linux-buff/libc-2.13.so00007fa1f3837000 4K rwx--/lib/x86_64-linux-buff/libc-2.13.so...
포맷에 대한 간단한 설명: 각 행은 세그먼트의 가상 메모리 주소로 시작합니다.그 다음에 세그먼트 크기, 권한 및 세그먼트의 소스가 나옵니다.이 마지막 항목은 파일 또는 mmap을 통해 할당된 메모리 블록을 나타내는 "anon"입니다.
맨 위부터 시작해서
- JVM 로더(입력 시 실행되는 프로그램)
java
이것은 매우 작습니다.실제 JVM 코드가 저장되어 있는 공유 라이브러리에 로드하는 것 뿐입니다. - Java 힙과 내부 데이터를 보관하고 있는 다수의 anon 블록.이것은 Sun JVM이기 때문에 힙은 여러 세대로 분할되며, 각 세대는 자체 메모리 블록입니다.JVM은 가상 메모리 공간을 할당하고 있습니다.
-Xmx
value: 연속된 힙을 가질 수 있습니다.그-Xms
value는 내부적으로 프로그램 시작 시 힙의 "사용 중"을 나타내고 제한에 도달하면 가비지 수집을 트리거하기 위해 사용됩니다. - 메모리 매핑된 JAR 파일. 이 경우 "JDK 클래스"를 유지하는 파일입니다.메모리 맵을 사용하면 JAR 내의 파일에 매우 효율적으로 액세스 할 수 있습니다(매회 처음부터 읽어낼 수 없습니다).Sun JVM은 클래스 패스의 모든 JAR을 메모리 매핑합니다.어플리케이션코드가 JAR에 액세스 할 필요가 있는 경우는, 메모리 매핑도 할 수 있습니다.
- 2개의 스레드에 대한 스레드당 데이터입니다.1M 블록은 스레드 스택입니다.4k 블록에 대한 자세한 설명은 없었지만 @ericsoe는 이 블록을 "가드 블록"으로 식별했습니다.이 블록에는 읽기/쓰기 권한이 없기 때문에 액세스하면 세그먼트 장애가 발생하고 JVM이 이를 포착하여 변환합니다.
StackOverFlowError
실제 앱의 경우 메모리 맵을 통해 이러한 엔트리가 수십 개에서 수백 개까지 반복됩니다. - 실제 JVM 코드를 보유하고 있는 공유 라이브러리 중 하나.이런 게 몇 개 있어요.
- C 표준 라이브러리의 공유 라이브러리.이는 JVM이 로딩하는 많은 것 중 하나이며 Java의 일부가 아닙니다.
공유 라이브러리는 특히 흥미롭습니다.각 공유 라이브러리에는 적어도2개의 세그먼트(라이브러리 코드를 포함한 읽기 전용 세그먼트와 라이브러리의 프로세스별 글로벌 데이터를 포함한 읽기 쓰기 세그먼트)가 있습니다(권한이 없는 세그먼트는 모릅니다.x64 Linux에서만 볼 수 있습니다).라이브러리의 읽기 전용 부분은 라이브러리를 사용하는 모든 프로세스 간에 공유할 수 있습니다. 예를 들어 다음과 같습니다.libc
공유 가능한 가상 메모리 공간이 150만 개 있습니다.
가상 메모리 크기가 중요한 시기
가상 메모리 맵에는 많은 내용이 포함되어 있습니다.일부는 읽기 전용이고 일부는 공유되며 일부는 할당되었지만 변경되지 않았습니다(예: 이 예에서는 거의 모든 4Gb 힙).다만, operating system은, 필요한 것만을 로드할 수 있을 정도로 스마트하기 때문에, 가상 메모리의 사이즈는 큰 관계가 없습니다.
가상 메모리 크기가 중요한 것은 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
filename을 클릭합니다.
Docker의 경우 Docker 파일에 추가합니다.
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_excessive_virtual_memory_usage?lang=en
상주 메모리는 메모리 누전이나 메모리 단편화와 유사한 방식으로 기는 것으로 알려져 있습니다.
JDK 버그 JDK-8193521 "glibc는 기본 구성으로 메모리를 낭비합니다"도 있습니다.
Google 또는 SO에서 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 - 클라이언트: 32m]
또, 애플리케이션의 실제 피크 메모리 사용량에 가능한 한 가까운 값으로 -Xmx(최대 히프 사이즈)를 설정할 필요가 있습니다.JVM의 기본 동작은 힙 크기를 최대까지 확장할 때마다 두 배로 증가한다고 생각합니다.32M 힙에서 시작하여 앱이 65M으로 피크가 되면 32M -> 64M -> 128M이 됩니다.
VM의 힙 확장에 대한 공격성을 줄이기 위해 다음과 같이 시도할 수도 있습니다.
-XX:MinHeapFreeRatio=40 확장을 방지하기 위해 GC 후 힙 빈 영역의 최소 백분율.
또한 몇 년 전 이 실험을 통해 기억나는 바로는 로드된 네이티브 라이브러리의 수가 최소 설치 공간에 큰 영향을 미쳤습니다.java.net를 로드하고 있습니다.소켓이 1500만 이상 추가되어 있는 것 같습니다(잘 기억하고 있지 않은 것 같습니다).
Sun JVM은 HotSpot에 많은 메모리가 필요하며 공유 메모리의 런타임 라이브러리에 매핑됩니다.
메모리에 문제가 있는 경우는, 임베디드에 적합한 다른 JVM 의 사용을 검토해 주세요.IBM에는 j9이 있으며 GNU 클래스 경로 라이브러리를 사용하는 오픈 소스 "jamvm"이 있습니다.또한 Sun은 SunSPOTS에서 Squak JVM을 실행하므로 다른 방법이 있습니다.
리소스가 제한된 시스템의 힙시스를 줄이는 방법 중 하나는 -XX:MaxHeapFreeRatio 변수를 사용하는 것입니다.이 값은 보통 70으로 설정되어 있으며, GC가 히프를 축소하기 전에 사용 가능한 히프의 최대 비율입니다.낮은 값으로 설정하면 jvisualvm profiler에 작은 힙시스가 보통 프로그램에서 사용되는 것을 확인할 수 있습니다.
편집: -XX:MaxHeapFreeRatio에 작은 값을 설정하려면 -XX:MinHeapFreeRatioEg도 설정해야 합니다.
java -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=25 HelloWorld
EDIT2: 같은 작업을 시작하고 실행하는 실제 응용 프로그램의 예를 추가했습니다.하나는 기본 파라미터로, 다른 하나는 10과 25를 파라미터로 합니다.이론적으로 java는 후자의 예에서 힙을 증가시키기 위해 더 많은 시간을 사용해야 하지만 실제 속도 차이는 느끼지 못했습니다.
마지막 최대 힙은 905, 사용된 힙은 378입니다.
마지막 최대 힙은 722, 사용된 힙은 378입니다.
어플리케이션이 리모트 데스크톱 서버에서 실행되기 때문에 실제로는 일부 조작이 되지 않습니다.많은 사용자가 동시에 실행할 수 있습니다.
그냥 생각일 뿐이지만, 옵션의 영향을 확인할 수도 있습니다.
이는 모든 프로세스에서 사용 가능한 주소 공간을 제한하기 때문에 실제 솔루션은 아니지만 제한된 가상 메모리를 사용하여 응용 프로그램의 동작을 확인할 수 있습니다.
Sun의 Java 1.4에는 메모리 크기를 제어하는 다음과 같은 인수가 있습니다.
-Xmsn 메모리 할당 풀의 초기 크기를 바이트 단위로 지정합니다.이 값은 1MB보다 큰 1024의 배수여야 합니다. 킬로바이트를 나타내려면 문자 k 또는 K를, 메가바이트를 나타내려면 문자 m 또는 M을 추가하십시오.기본값은 2MB입니다.예:
-Xms6291456 -Xms6144k -Xms6m
-Xmxn 메모리 할당 풀의 최대 크기를 바이트 단위로 지정합니다.이 값은 2MB보다 큰 1024의 배수여야 합니다. 킬로바이트를 나타내려면 문자 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에 필요한 메모리 양을 구성할 수 없습니다.단, 이 메모리는 가상 메모리이며 상주 메모리가 아니기 때문에 실제로 사용하지 않더라도 손상되지 않고 그대로 유지됩니다.
또한 메모리 설치 공간이 더 작은 Sun과 다른 JVM을 사용해 볼 수도 있지만 여기서는 권장할 수 없습니다.
언급URL : https://stackoverflow.com/questions/561245/virtual-memory-usage-from-java-under-linux-too-much-memory-used
'source' 카테고리의 다른 글
v-for 구조 간의 차이 (0) | 2022.08.16 |
---|---|
웹 앱에 C를 사용하지 않는 이유는 무엇입니까? (0) | 2022.08.15 |
char 배열에 저장된 머신 코드를 호출하려면 어떻게 해야 합니다. (0) | 2022.08.15 |
어떻게 sprintf을 사용하여 문자열을 추가할 수 있는? (0) | 2022.08.15 |
게터나 세터는 디자인이 좋지 않은가요?모순된 조언이 표시됨 (0) | 2022.08.15 |