2011-05-13 7 views
12

인덱스 파일에 대해 메모리 매핑 된 IO를 사용하고 있지만 대부분 비어있는 경우 파일의 크기를 조정할 수 없다는 것이 문제입니다. 이전메모리 매핑 된 파일을 자릅니다.

어딘가에 :

MappedByteBuffer map = raf.getChannel().map(MapMode.READ_WRITE, 0, 1 << 30); 
raf.close(); 
// use map 
map.force(); 
map = null; 

조정 :

for (int c = 0; c < 100; c++) { 
    RandomAccessFile raf = new RandomAccessFile(indexFile, "rw"); 
    try { 
     raf.setLength(newLen); 
     if (c > 0) LOG.warn("used " + c + " iterations to close mapped byte buffer"); 
     return; 
    } catch (Exception e) { 
     System.gc(); 
     Thread.sleep(10); 
     System.runFinalization(); 
     Thread.sleep(10); 
    } finally { 
     raf.close(); 
    } 
} 

Windows 또는를 사용하여

리눅스 32 비트 나는 종종 매핑 해제 문제가 있지만, 64 비트 Linux 프로덕션 환경에서 모든 작동하는 것 같다 경고없이, 파일은 원래 크기를 유지합니다.

왜 이런 일이 발생했는지 그리고 문제를 어떻게 해결할 수 있는지 설명 할 수 있습니까?

+0

저는 어떻게 든 NFS, 캐싱 또는 타이밍에 의존한다는 것을 두려워합니다. 실제 간섭없이 해결 된 것으로 보입니다. (그냥 로깅을 추가하고 대기하고 지금은 작동합니다.)이후 truncationg 후에 커다란 화제를 한 파일들과 지금까지 건드리지 않은 파일들도 정확한 크기를 가지고 있습니다. 어쩌면 truncate 후 새 파일 크기의 로깅은 일부 nfs 캐시를 업데이트합니다. – rurouni

+0

문제는 [파일을 해제하는 방법] (http://stackoverflow.com/questions/2972986)과 유사합니다. 특히 [버그 # 4724038] (http://bugs.sun.com/view_bug)를 참조하십시오. do? bug_id = 4724038). –

답변

7

매핑 된 바이트 버퍼를 닫는 데 신뢰할 수없는 방법을 사용하고 있습니다 (System.gc()System.runFinalization()에 대한 백개의 호출이 당신을 보장하지는 않습니다). 불행하게도 그 작업을 수행하는 자바 API에는 신뢰할 수있는 방법은 없지만, 일 JVM에 (아마도 너무 일부 다른 사람에) 다음과 같은 코드를 사용할 수 있습니다 : 그것은 JVM 의존 물론

public void unmapMmaped(ByteBuffer buffer) { 
    if (buffer instanceof sun.nio.ch.DirectBuffer) { 
    sun.misc.Cleaner cleaner = ((sun.nio.ch.DirectBuffer) buffer).cleaner(); 
    cleaner.clean(); 
    } 
} 

을 당신은해야한다 썬이 sun.nio.ch.DirectBuffer 또는 sun.misc.Cleaner을 호환되지 않는 방식으로 변경하기로 결정한 경우 코드를 수정할 준비가되었습니다 (실제로 실제로 발생하지는 않을 것입니다).

3

이것은 이전 답변의 보완 된 설명이며 완전히 정확합니다.

JDK 1.7은 sun.misc.Cleaner의 사용에 대해이 네임 스페이스의 클래스가 JDK의 공식적인 부분이 아니며 향후 사라질 수 있다고 말합니다. 그러나 1.7 현재 그들은 여전히 ​​존재합니다.

.clean() 방법을 사용할 수없는 경우 대체 방법으로 System.gc()을 사용할 수 있지만 "해킹"으로 인식해야하므로주의해야합니다.

은 참조되지 않은 매핑을 강제로 닫을 수 없지만 실제로는 종종 정리가 발생합니다. 32 비트 Linux (및 Solaris) 환경에서는 System.gc()에 대한 첫 번째 호출 또는 두 번째 호출 중에 버퍼가 모든 테스트 중에 릴리스됨을 보여줍니다. 그러나 Windows에서의 동작은 다릅니다. 대부분의 경우 모든 매핑은 System.gc()에 대한 두 번째 호출이 끝날 때 해제되지만 때로는 3 번의 호출이 필요합니다. 더 많은 호출이 요구되는 경우가 종종 있으며, 더 많은 호출 횟수가 줄어들 필요가 있습니다. 테스트에서 4 회의 통화가 모두 필요하다는 것을 나타낼 수 있으며 한 달 후에 실패 할 수 있다는 점에서 사기성이있을 수 있습니다. 5 통화는 6 개월 만에 실패로 이어질 수있는 것처럼 보일 수 있습니다.

try/catch 주위에 FileChannel.truncate() 블록을 사용하여 테스트가 실패한 경우 작업을 다시 시도하여 맵이 해제되었는지 테스트 할 수 있습니다. 특정 힙 구성이 가비지 컬렉터가 매핑을 정리하지 못하게하는 병적 인 경우가 있기 때문에 루프를 무한대로 수행 할 수 없습니다. 그러나 약 10의 루프가 거의 모든 경우에 적용됩니다. 객체가 그 지점까지지나 가지 않으면 아무데도 가지 않고 응용 프로그램은 포기해야합니다. 이는 불충분 해 보일 수도 있지만 실제로는 극히 드문 경우이며 클리너를 지원하지 않는 JVM에서만 문제가됩니다.

관련 문제