이것은 오랫동안 Java에 대한 불만 이었지만, 의미가 없으며 일반적으로 잘못된 정보를보고 있습니다. 일반적인 문구는 "Hello World on Java는 10MB가 필요합니다! 왜 필요한가요?" 자, 여기 64 비트 JVM에서 Hello World가 적어도 한 가지 형태의 측정으로 4 기가 바이트 이상을 차지한다고 주장하는 방법이 있습니다.
java -Xms1024m -Xmx4096m com.example.Hello
메모리를 측정하는 다른 방법
Linux에서 top 명령은 여러 가지 메모리 번호를 제공합니다. Hello World 예제에 대한 내용은 다음과 같습니다.
PID 사용자 PR NI VIRT RES SHR S % CPU % MEM TIME + 명령
2120 kgregory 20 4373m 15m 7152 S 0 0.2 0 : 00.10 자바
- VIRT는 가상 메모리 공간입니다. 가상 메모리 맵의 모든 항목의 합계입니다 (아래 참조). 그렇지 않은 경우를 제외하고는 크게 의미가 없습니다 (아래 참조).
- RES는 상주 세트 크기 : 현재 RAM에 상주하는 페이지 수입니다. 거의 모든 경우에, 이것은 "너무 큰"이라고 말할 때 사용해야하는 유일한 숫자입니다. 그러나 특히 Java에 관해 이야기 할 때 여전히 좋은 숫자는 아닙니다.
- SHR은 다른 프로세스와 공유되는 상주 메모리의 양입니다. Java 프로세스의 경우 이는 일반적으로 공유 라이브러리 및 메모리 맵핑 JAR 파일로 제한됩니다. 이 예에서는 Java 프로세스가 하나만 실행되었으므로 7k는 OS에서 사용하는 라이브러리의 결과라고 생각합니다.
- SWAP는 기본적으로 켜져 있지 않으며 여기에 표시되지 않습니다. 실제로 스왑 공간에 있는지 여부에 관계없이 디스크에 현재 상주하는 가상 메모리의 양을 나타냅니다 . OS는 활성 페이지를 RAM에 보관하는 데 매우 적합하며 (1) 더 많은 메모리를 구매하거나 (2) 프로세스 수를 줄이는 것만으로도이 수를 무시하는 것이 가장 좋습니다.
Windows 작업 관리자의 상황은 좀 더 복잡합니다. Windows XP에는 "메모리 사용"및 "가상 메모리 크기"열이 있지만 공식 설명서 는 그 의미에 대해 침묵합니다. Windows Vista 및 Windows 7은 더 많은 열을 추가하며 실제로 문서화되어 있습니다. 이 중 "작업 세트"측정이 가장 유용합니다. Linux의 RES와 SHR의 합과 거의 같습니다.
가상 메모리 맵 이해
프로세스가 소비하는 가상 메모리는 프로세스 메모리 맵에있는 모든 것의 총합입니다. 여기에는 데이터 (예 : Java 힙)뿐만 아니라 프로그램에서 사용하는 모든 공유 라이브러리 및 메모리 매핑 파일도 포함됩니다. Linux에서는 pmap 명령을 사용하여 프로세스 공간에 매핑 된 모든 항목을 볼 수 있습니다 (여기서부터는 Linux를 참조 할 것입니다. 왜냐하면 내가 사용하는 것이므로 이에 해당하는 도구가 있다고 확신합니다). Windows). 다음은 "Hello World"프로그램의 메모리 맵에서 발췌 한 것입니다. 전체 메모리 맵의 길이는 100 줄을 넘으며 천줄 목록을 갖는 것은 드문 일이 아닙니다.
0000000040000000 36K rx-/usr/local/java/jdk-1.6-x64/bin/java
0000000040108000 8K rwx-/usr/local/java/jdk-1.6-x64/bin/java
0000000040eba000 676K rwx-- [아논]
00000006fae00000 21248K rwx-- [아논]
00000006fc2c0000 62720K rwx-- [아논]
0000000700000000 699072K rwx-- [아논]
000000072aab0000 2097152K rwx-- [anon]
00000007aaab0000 349504K rwx-- [아논]
00000007c0000000 1048576K rwx-- [아논]
...
00007fa1ed00d000 1652K r-xs- /usr/local/java/jdk-1.6-x64/jre/lib/rt.jar
...
00007fa1ed1d3000 1024K rwx-- [anon]
00007fa1ed2d3000 4K ----- [아논]
00007fa1ed2d4000 1024K rwx-- [anon]
00007fa1ed3d4000 4K ----- [아논]
...
00007fa1f20d3000 164K rx-/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 rx-/lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3634000 2044K ----- /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3833000 16K rx-/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 코드가 저장된 공유 라이브러리에로드하기 만하면됩니다.
- Java 힙 및 내부 데이터를 보유하는 많은 anon 블록. 이것은 Sun JVM이므로 힙은 여러 세대로 나뉘며 각 세대는 자체 메모리 블록입니다. JVM은
-Xmx
값을 기반으로 가상 메모리 공간을 할당 합니다. 이를 통해 연속적인 힙을 가질 수 있습니다. -Xms
값은 프로그램 시작 및 트리거 가비지 컬렉션에 그 한계가 접근로 할 때 "사용"얼마나 많은 힙의 말을 내부적으로 사용됩니다.
- 메모리 맵핑 JAR 파일 (이 경우 "JDK 클래스"를 보유하는 파일) JAR을 메모리 맵핑 할 때, 매번 처음부터 파일을 읽는 것과는 달리 JAR 파일 내의 파일에 매우 효율적으로 액세스 할 수 있습니다. Sun JVM은 클래스 경로의 모든 JAR을 메모리 매핑합니다. 애플리케이션 코드가 JAR에 액세스해야하는 경우 메모리 맵핑 할 수도 있습니다.
- 두 개의 스레드에 대한 스레드 별 데이터. 1M 블록은 스레드 스택입니다. 4k 블록에 대한 좋은 설명은 없지만 @ericsoe는이를 "guard 블록"으로 식별했습니다. 읽기 / 쓰기 권한이 없으므로 액세스하면 세그먼트 오류가 발생하고 JVM이이를 포착하여 번역합니다. 에
StackOverFlowError
. 실제 응용 프로그램의 경우 수백 개의 항목이 메모리 맵을 통해 반복되지 않으면 수십 개가 표시됩니다.
- 실제 JVM 코드를 보유하는 공유 라이브러리 중 하나입니다. 이 중 몇 가지가 있습니다.
- C 표준 라이브러리의 공유 라이브러리 이것은 엄밀히 Java의 일부가 아닌 JVM이로드하는 많은 것 중 하나 일뿐입니다.
공유 라이브러리는 특히 흥미 롭습니다. 각 공유 라이브러리에는 라이브러리 코드가 포함 된 읽기 전용 세그먼트와 라이브러리에 대한 전역 프로세스 별 데이터를 포함하는 읽기 / 쓰기 세그먼트가 있습니다. 권한이없는 세그먼트는 x64 Linux에서만 볼 수 있습니다). 라이브러리의 읽기 전용 부분은 라이브러리를 사용하는 모든 프로세스간에 공유 할 수 있습니다. 예를 들어 libc
공유 할 수있는 1.5M의 가상 메모리 공간이 있습니다.
가상 메모리 크기는 언제 중요합니까?
가상 메모리 맵에는 많은 것들이 포함되어 있습니다. 일부는 읽기 전용이며, 일부는 공유되며, 일부는 할당되었지만 절대 손대지 않습니다 (예 :이 예에서 거의 모든 4Gb 힙). 그러나 운영 체제는 필요한 것만로드하기에 충분히 영리하므로 가상 메모리 크기는 크게 관련이 없습니다.
가상 메모리 크기가 중요한 곳은 32 비트 운영 체제에서 실행중인 경우 2Gb (또는 경우에 따라 3Gb)의 프로세스 주소 공간 만 할당 할 수 있습니다. 이 경우 부족한 리소스를 처리하고 있으며 대용량 파일을 메모리 매핑하거나 많은 스레드를 만들려면 힙 크기를 줄이는 등의 절충을해야 할 수도 있습니다.
그러나 64 비트 컴퓨터가 어디에나 있기 때문에 가상 메모리 크기가 완전히 관련이없는 통계가되기까지는 오래 걸리지 않을 것이라고 생각합니다.
상주 세트 크기는 언제 중요합니까?
상주 세트 크기는 실제로 RAM에있는 가상 메모리 공간의 해당 부분입니다. RSS가 전체 실제 메모리의 상당 부분으로 성장하면 걱정할 시간입니다. RSS가 모든 실제 메모리를 차지하기 위해 성장하고 시스템이 스와핑을 시작하면 걱정할 시간이 지났습니다.
그러나 RSS는 특히 약간로드 된 시스템에서 오해의 소지가 있습니다. 운영 체제는 프로세스에서 사용하는 페이지를 회수하기 위해 많은 노력을 기울이지 않습니다. 그렇게하면 얻을 수있는 이점이 거의 없으며 프로세스가 나중에 페이지에 닿으면 페이지 오류가 발생할 가능성이 있습니다. 결과적으로 RSS 통계에는 활성화되지 않은 많은 페이지가 포함될 수 있습니다.
결론
교환하지 않는 한 다양한 메모리 통계가 알려주는 것에 지나치게 걱정하지 마십시오. 계속해서 증가하는 RSS는 일종의 메모리 누수를 나타낼 수 있다는 경고가 있습니다.
Java 프로그램을 사용하면 힙에서 발생하는 일에주의를 기울이는 것이 훨씬 중요합니다. 사용 된 총 공간이 중요하며이를 줄이기 위해 수행 할 수있는 몇 가지 단계가 있습니다. 더 중요한 것은 가비지 수집에 소요되는 시간과 힙의 어떤 부분이 수집되는지입니다.
디스크 (즉, 데이터베이스)에 액세스하는 것은 비용이 많이 들고 메모리는 저렴합니다. 서로 교환 할 수 있다면 그렇게하십시오.