Microsoft MVP성태의 닷넷 이야기
Java: 18. Java의 Memory Mapped File 자원 반환이 안되는 문제 [링크 복사], [링크+제목 복사],
조회: 14528
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

Java의 Memory Mapped File 자원 반환이 안되는 문제

Java에서는 다음의 글에 소개된 데로,

Memory Mapped File
; http://www.javacodex.com/Files/Memory-Mapped-File

이렇게 MMF를 사용한다고 합니다.

import java.io.*;
import java.nio.*;
import java.nio.channels.*;

public class MemoryMappedFile {

   public static void main(String[] args) {

      try {

         File file = new File("/tmp/myfile.txt");
         FileChannel fc = new RandomAccessFile(file, "rw").getChannel();
         ByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, (int) fc.size());

         // print contents of ByteBuffer //
         while (bb.hasRemaining()) {
            System.out.print((char) bb.get());
         }

      } catch (IOException e) {
         System.out.println("Error: " + e.toString());
      }
   }
}

그런데, 사내 자바 개발자가 메모리 맵 파일이 GC가 발생하기 전까지는 어떻게 해도 해제할 수 없다는 문제를 경험했습니다. 이를, 간단하게 다음의 코드로 재현해 볼 수 있습니다.

package com.company;

import java.io.*;
import java.nio.*;
import java.nio.channels.*;

public class Main {

    public static void main(String[] args) {

        try {
            while (true) {

                UseMemoryMap();
                if (System.in.available() != 0) {
                    break;
                }

                Thread.sleep(1000);
            }
        } catch (IOException e) {
        } catch (InterruptedException e) {
        }
    }

    public static void UseMemoryMap() {
        try {
            File file = new File("c:/temp/Installer.exe"); // 1MB짜리 파일
            FileChannel fc = new RandomAccessFile(file, "rw").getChannel();
            MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, (int) fc.size());

            bb.clear();
            fc.close(); // clear / close를 해도 메모리는 지속적으로 증가!
        } catch (IOException e) {
            System.out.println("Error: " + e.toString());
        }
    }
}

위의 자바 코드는 1MB 파일에 대한 MMF 설정을 1초마다 반복하는 바람에 "Private Bytes"가 끊임없이 증가하는 문제가 있습니다. 실제로 위의 프로그램이 실행되는 동안 "Process Explorer"로 File Handle의 상황을 모니터링해 보면 다음과 같이 해제되지 않고 끊임없이 증가하는 것을 볼 수 있습니다.

java_mem_map_leak_1.png

이 문제를 해결하려면 명시적으로 System.gc()를 호출해야 하는데, 가령 다음과 같이 위의 코드를 수정해 주면,

try {
    while (true) {

        UseMemoryMap();
        if (System.in.available() != 0) {
            break;
        }

        Thread.sleep(1000);
        System.gc();
    }
} catch (IOException e) {
} catch (InterruptedException e) {
}

메모리 증가 문제가 없어집니다.

이 문제가 발생하는 원인은 2가지가 복합적으로 작용해 발생합니다. 하나는, Java 측에서 MMF 자원을 명시적으로 해제하는 방법을 제공하지 않는다는 점입니다. 또 하나는, Java의 GC 입장에서 GC Heap이 아닌 Native 메모리에 대한 증가는 System.gc 호출을 발생시키지 않는다는 점입니다.

이로 인해, 특정 자바 프로그램이 지속적으로 MMF를 열고/닫는 작업을 하지만 딱히 GC Heap에 대한 변동이 없는 상황이라면 저런 식으로 메모리가 무한정 소비되는 문제가 발생하는 것입니다.




다음은 사내 개발자가 제시한 부가적인 해결 방법입니다.

How to unmap a file from memory mapped using FileChannel in java?
; http://stackoverflow.com/questions/2972986/how-to-unmap-a-file-from-memory-mapped-using-filechannel-in-java

덧글에 보면, SUN JDK에서의 우회 방법이 나옵니다. 또한, 주기적으로 System.gc를 하라는 것도 나오는데 이는 "-XX:+DisableExplicitGC" 옵션이 있는 경우 동작하지 않는다고 합니다. 따라서, (호스트 환경을 장담할 수 없는) 라이브러리 개발자가 써먹기에는 부담스럽습니다.




그렇다면 혹시 닷넷은 어떨까요? 다행히, 닷넷은 MemoryMappedFile.Dispose 메서드가 제공되는데 이를 호출하면 Native 리소스가 잘 해제됩니다.

아래는 참고 글입니다.

Memory-mapped File은 Private Bytes 크기에 포함될까요?
; https://www.sysnet.pe.kr/2/0/11159




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 4/17/2017]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 16  17  18  19  20  21  22  23  24  25  26  27  28  29  [30]  ...
NoWriterDateCnt.TitleFile(s)
12885정성태12/20/20217823오류 유형: 776. uwsgi-plugin-python3 환경에서 MySQLdb 사용 환경
12884정성태12/20/20216870개발 환경 구성: 620. Windows 10+에서 WMI root/Microsoft/Windows/WindowsUpdate 네임스페이스 제거
12883정성태12/19/20217733오류 유형: 775. uwsgi-plugin-python3 환경에서 "ModuleNotFoundError: No module named 'django'" 오류 발생
12882정성태12/18/20216838개발 환경 구성: 619. Windows Server에서 WSL을 위한 리눅스 배포본을 설치하는 방법
12881정성태12/17/20217338개발 환경 구성: 618. WSL Ubuntu 20.04에서 파이썬을 위한 uwsgi 설치 방법 (2)
12880정성태12/16/20217148VS.NET IDE: 170. Visual Studio에서 .NET Core/5+ 역어셈블 소스코드 확인하는 방법
12879정성태12/16/202113398오류 유형: 774. Windows Server 2022 + docker desktop 설치 시 WSL 2로 선택한 경우 "Failed to deploy distro docker-desktop to ..." 오류 발생
12878정성태12/15/20218473개발 환경 구성: 617. 윈도우 WSL 환경에서 같은 종류의 리눅스를 다중으로 설치하는 방법
12877정성태12/15/20217113스크립트: 36. 파이썬 - pymysql 기본 예제 코드
12876정성태12/14/20216955개발 환경 구성: 616. Custom Sources를 이용한 Azure Monitor Metric 만들기
12875정성태12/13/20216626스크립트: 35. python - time.sleep(...) 호출 시 hang이 걸리는 듯한 문제
12874정성태12/13/20216617오류 유형: 773. shell script 실행 시 "$'\r': command not found" 오류
12873정성태12/12/20217772오류 유형: 772. 리눅스 - PATH에 등록했는데도 "command not found"가 나온다면?
12872정성태12/12/20217583개발 환경 구성: 615. GoLang과 Python 빌드가 모두 가능한 docker 이미지 만들기
12871정성태12/12/20217665오류 유형: 771. docker: Error response from daemon: OCI runtime create failed
12870정성태12/9/20216223개발 환경 구성: 614. 파이썬 - PyPI 패키지 만들기 (4) package_data 옵션
12869정성태12/8/20218514개발 환경 구성: 613. git clone 실행 시 fingerprint 묻는 단계를 생략하는 방법
12868정성태12/7/20217090오류 유형: 770. twine 업로드 시 "HTTPError: 400 Bad Request ..." 오류 [1]
12867정성태12/7/20216757개발 환경 구성: 612. 파이썬 - PyPI 패키지 만들기 (3) entry_points 옵션
12866정성태12/7/202114143오류 유형: 769. "docker build ..." 시 "failed to solve with frontend dockerfile.v0: failed to read dockerfile ..." 오류
12865정성태12/6/20216838개발 환경 구성: 611. 파이썬 - PyPI 패키지 만들기 (2) long_description, cmdclass 옵션
12864정성태12/6/20215288Linux: 46. WSL 환경에서 find 명령을 사용해 파일을 찾는 방법
12863정성태12/4/20217221개발 환경 구성: 610. 파이썬 - PyPI 패키지 만들기
12862정성태12/3/20215934오류 유형: 768. Golang - 빌드 시 "cmd/go: unsupported GOOS/GOARCH pair linux /amd64" 오류
12861정성태12/3/20218157개발 환경 구성: 609. 파이썬 - "Windows embeddable package"로 개발 환경 구성하는 방법
12860정성태12/1/20216277오류 유형: 767. SQL Server - 127.0.0.1로 접속하는 경우 "Access is denied"가 발생한다면?
... 16  17  18  19  20  21  22  23  24  25  26  27  28  29  [30]  ...