2008-08-26 3 views
3

ByteBuffersFileChannels을 사용하여 이진 데이터를 파일에 기록합니다. 대용량 파일이나 연속적으로 여러 파일을 처리 할 때 OutOfMemoryError 예외가 발생합니다. NIO와 함께 Bytebuffers을 사용하는 것이 깨 졌으므로이 부분을 읽었으며 피해야합니다. 여러분 중 누구도 이미 이런 종류의 문제에 직면 해 있으며 많은 양의 바이너리 데이터를 자바 파일에 효율적으로 저장하는 해결책을 찾았습니까?Bytebuffers 및 NIO를 사용할 때 OutOfMemoryError를 피하는 방법은 무엇입니까?

jvm 옵션 -XX:MaxDirectMemorySize가는 방법이 있습니까?

답변

6

모든 데이터를 한 번에 포함하는 거대한 ByteBuffer를 만들지는 않겠습니다. 훨씬 작은 ByteBuffer를 만들고 데이터로 채운 다음이 데이터를 FileChannel에 씁니다. 그런 다음 ByteBuffer를 재설정하고 모든 데이터가 기록 될 때까지 계속하십시오. 당신이 무작위 방식으로에있는 파일에 액세스하는 경우

1

다음 당신은 문제가 ;-)

을 가지고하지만 당신은 큰 파일을 작성하는 경우, 당신이 심각 를해야 (이 쓰기, 건너 뛰기, 여기 읽어 뒤로 이동) 스트림 사용을 고려하십시오. java.io.FileOutputStream는, float, ints, Strings, 또는 직렬화 가능 오브젝트의 기입을 편리하게하기 위해서, byte 이후에 파일 바이트를 직접 기입하거나 다른 스트림 (즉, DataOutputStream, ObjectOutputStream)에 랩하는 것에 직접적으로 사용할 수 있습니다. 파일을 읽는 데에도 비슷한 클래스가 있습니다.

스트림은 임의의 작은 파일에서 임의의 큰 파일 을 조작 할 수있는 편리함을 제공합니다. 대부분의 경우 파일 시스템에 액세스하는 기본 방법입니다.

4

'직접 버퍼'라고도하는 Java의 Mapped Byte Buffers을 확인하십시오. 기본적으로이 메커니즘은 OS의 가상 메모리 페이징 시스템을 사용하여 버퍼를 디스크에 직접 매핑합니다. 운영 체제는 디스크와 메모리간에 바이트를 자동으로 매끄럽게 자동으로 관리하므로 가상 시스템 옵션을 변경하는 것에 대해 걱정할 필요가 없습니다. 또한 NIO의 전통적인 Java 스트림 기반 I/O에 비해 향상된 성능을 이상한 해킹없이 활용할 수 있습니다. 내가 생각할 수있는

유일한 두 어획량은 다음과 같습니다

32 비트 시스템에서
  1. 는, 모든 매핑 바이트 버퍼만을위한 4기가바이트에서 총으로 제한됩니다. (실제로는 내 응용 프로그램의 한계이며 이제는 64 비트 아키텍처에서 실행됩니다.)
  2. 구현은 JVM에 따라 다르며 요구 사항은 아닙니다. Sun의 JVM을 사용하는데 아무 문제가 없지만 YMMV.

커크 페퍼 다인 (다소 유명한 자바 성능 전문가)는 좀 더 MBB 세부 사항이있는 웹 사이트, www.JavaPerformanceTuning.com으로 참여하고있다 : NIO Performance Tips

+0

매핑 된 모든 바이트 버퍼 (내 응용 프로그램 또는 모든 OS의 ??)에 대한 제한이 있음을 알려 주셔서 감사합니다. 내 경우에는 주변의 파일에 대해 하나의 MappedByteBuffer를 시도해도 어리석은 OutOfMemoryException이 발생합니다. 1.6GB! 하지만 왜? 남은 공간이 얼마나 넓은 지 어떻게 알 수 있을까요 ?? 도움! – Zordid

+0

@ Zordid Ummm ...'MappedByteBuffer' (매핑 된 키!)는 일반적으로 'OutOfMemoryException'을 발생시키지 않습니다. 뭔가 다른 문제가 있습니다. StackOverflow에 새로운 질문을 만드는 것이 좋습니다 ... 코드로! 누군가 당신을 도울 수 있습니다. –

0

는 이전의 두 반응은 꽤 합리적인 것 같다. 명령 줄 스위치가 작동하는지 여부는 메모리 사용량이 얼마나 빨리 한계에 도달하는지에 달려 있습니다. 사용할 수있는 메모리와 메모리를 최소한 3 배로 늘릴 수있는 충분한 RAM과 가상 메모리가없는 경우 주어진 대체 제안 사항 중 하나를 사용해야합니다.

0

transferFrom 메서드를 사용하면 채널에 점진적으로 작성한다고 가정 할 때 이전 도움말과 마찬가지로 모든 것을 한꺼번에 작성하지 않는 것이 좋습니다.

0

이것은 특정 JDK 공급 업체 및 버전에 따라 다를 수 있습니다.

일부 Sun JVM에서는 GC에 버그가 있습니다. 직접 메모리가 부족하면 주 힙에서 GC가 트리거되지 않지만 직접 메모리는 주 힙의 가비지 직접 ByteBuffers에 의해 고정됩니다. 주 힙이 대부분 비어 있으면 많은 시간이 오래 수집되지 않습니다.

JVM이 사용자를 대신하여 직접 버퍼를 생성 할 수 있기 때문에 직접 버퍼를 직접 사용하지 않아도 화상을 입을 수 있습니다. 예를 들어 비 직접적인 ByteBuffer를 SocketChannel에 작성하면 실제 I/O 작업에 사용할 직접 버퍼가 만들어집니다.

해결 방법은 소수의 직접 버퍼를 직접 사용하고 재사용을 위해 보관하십시오.

관련 문제