Java MappedByteBuffers를 사용하여 "이 명령을 처리 할 수있는 충분한 저장 공간이 없다"는 이유는 무엇입니까?
-
20-09-2019 - |
문제
디스크 기반 파일을 사용하고있는 두 배의 배열과 처리 할 수있는 MappedByteBuffers 목록을 사용하고 있습니다. 이 질문 더 많은 배경. Java 1.5를 사용하여 Windows XP에서 실행 중입니다.
다음은 파일에 대한 버퍼를 할당하는 내 코드의 핵심 부분입니다 ...
try
{
// create a random access file and size it so it can hold all our data = the extent x the size of a double
f = new File(_base_filename);
_filename = f.getAbsolutePath();
_ioFile = new RandomAccessFile(f, "rw");
_ioFile.setLength(_extent * BLOCK_SIZE);
_ioChannel = _ioFile.getChannel();
// make enough MappedByteBuffers to handle the whole lot
_pagesize = bytes_extent;
long pages = 1;
long diff = 0;
while (_pagesize > MAX_PAGE_SIZE)
{
_pagesize /= PAGE_DIVISION;
pages *= PAGE_DIVISION;
// make sure we are at double boundaries. We cannot have a double spanning pages
diff = _pagesize % BLOCK_SIZE;
if (diff != 0) _pagesize -= diff;
}
// what is the difference between the total bytes associated with all the pages and the
// total overall bytes? There is a good chance we'll have a few left over because of the
// rounding down that happens when the page size is halved
diff = bytes_extent - (_pagesize * pages);
if (diff > 0)
{
// check whether adding on the remainder to the last page will tip it over the max size
// if not then we just need to allocate the remainder to the final page
if (_pagesize + diff > MAX_PAGE_SIZE)
{
// need one more page
pages++;
}
}
// make the byte buffers and put them on the list
int size = (int) _pagesize ; // safe cast because of the loop which drops maxsize below Integer.MAX_INT
int offset = 0;
for (int page = 0; page < pages; page++)
{
offset = (int) (page * _pagesize );
// the last page should be just big enough to accommodate any left over odd bytes
if ((bytes_extent - offset) < _pagesize )
{
size = (int) (bytes_extent - offset);
}
// map the buffer to the right place
MappedByteBuffer buf = _ioChannel.map(FileChannel.MapMode.READ_WRITE, offset, size);
// stick the buffer on the list
_bufs.add(buf);
}
Controller.g_Logger.info("Created memory map file :" + _filename);
Controller.g_Logger.info("Using " + _bufs.size() + " MappedByteBuffers");
_ioChannel.close();
_ioFile.close();
}
catch (Exception e)
{
Controller.g_Logger.error("Error opening memory map file: " + _base_filename);
Controller.g_Logger.error("Error creating memory map file: " + e.getMessage());
e.printStackTrace();
Clear();
if (_ioChannel != null) _ioChannel.close();
if (_ioFile != null) _ioFile.close();
if (f != null) f.delete();
throw e;
}
두 번째 또는 세 번째 버퍼를 할당 한 후 제목에 언급 된 오류가 발생합니다.
나는 그것이 인접한 메모리와 관련이 있다고 생각 했으므로 다양한 크기와 페이지의 페이지로 시도했지만 전반적인 이점은 없습니다.
정확히 무엇을 하는가 "이 명령을 처리하기에 충분한 스토리지를 사용할 수 없습니다" 무엇을 의미하고 무엇이든 할 수 있습니까?
나는 MappedBytebuffers의 요점이 힙에 맞는 것보다 더 큰 구조물을 처리 할 수있는 능력이라고 생각했으며, 그들이 기억하는 것처럼 취급합니다.
단서가 있습니까?
편집하다:
아래 답변 (@ADSK)에 대한 응답으로 코드를 변경하여 한 번에 단일 활성 MAPPEDBYTEBUFFER 이상을 갖지 못했습니다. 현재 파일의 영역을 참조하면 기존 맵을 정크하고 새 맵을 만듭니다. 약 3 개의지도 작업 후에도 여전히 동일한 오류가 발생합니다.
MappedBytebuffers를 수집하지 않는 GC로 인용 된 버그는 여전히 JDK 1.5에서 문제가되는 것 같습니다.
해결책
나는 MappedBytebuffers의 요점이 힙에 맞는 것보다 더 큰 구조물을 처리 할 수있는 능력이라고 생각했으며, 그들이 기억하는 것처럼 취급합니다.
아닙니다. 아이디어는 당신이 충분한 메모리가 있고 64 비트 JVM을 사용하고 있다는 가정하에 2 ** 31 더블 이상을 다룰 수 있도록하는 것입니다.
(나는 이것이 후속 질문이라고 가정합니다. 이 질문.)
편집하다: 더 많은 설명이 필요합니다.
많은 한계가 있습니다.
Java는 그 근본적인 제한을 가지고 있습니다
length
배열의 속성 및 배열 인덱스는 유형이 있습니다.int
. 이것은 사실과 결합했습니다int
서명하고 배열은 음수 크기를 가질 수 없음 가장 큰 배열에 가질 수 있음을 의미합니다.2**31
집단. 이 제한은 32 비트 및 64 비트 JVM에 적용됩니다. 그것은 Java 언어의 기본 부분입니다.char
값은에서 나옵니다0
에게65535
.32 비트 JVM 사용 A (이론적) 상한
2**32
JVM에서 주소를 지정할 수있는 바이트 수. 여기에는 전체 힙, 코드 및 사용하는 라이브러리 클래스, JVM의 기본 코드 코어, 매핑 버퍼에 사용되는 메모리가 포함됩니다. (사실, 플랫폼에 따라 OS 5월 당신에게 상당히 적습니다2**32
주소 공간 인 경우 바이트.)Java Command Line에서 제공하는 매개 변수는 JVM이 얼마나 많은 힙 메모리를 결정합니다. 허용하다 사용할 응용 프로그램. 사용에 메모리 매핑
MappedByteBuffer
객체는 이것에 포함되지 않습니다.OS가 제공하는 메모리의 양은 (Linux/UNIX에) 구성된 총 스왑 공간, '프로세스'제한 등에 따라 다릅니다. 비슷한 한계는 아마도 창에 적용될 수 있습니다. 물론 호스트 OS가 64 비트 기능이 가능한 경우 64 비트 JVM 만 실행할 수 있으며 64 비트 유능한 하드웨어를 사용하고 있습니다. (펜티엄이 있다면 운이 좋지 않습니다.)
마지막으로, 시스템의 물리적 기억의 양이 작용합니다. 이론적으로는 JVM에 기계의 물리적 메모리보다 더 큰 힙 등을 사용하도록 요청할 수 있습니다. 실제로, 이것은 a입니다 나쁜 생각. 가상 메모리를 과도하게 할당하면 시스템이 삭감되고 응용 프로그램 성능이 바닥을 통과합니다.
테이크 아웃은 이것입니다.
32 비트 JVM을 사용하는 경우 아마도
2**31
그리고2**32
주소가 가능한 메모리의 바이트. 그것은 최대 사이에 충분한 공간입니다2**29
그리고2**30
배열 또는 매핑 버퍼를 사용하든 복식.64 비트 JVM을 사용하는 경우 단일 배열을 나타낼 수 있습니다.
2**31
더블스. 매핑 된 버퍼의 이론적 한계가 될 것입니다2**63
바이트 또는2**61
복식이지만 실질적인 한계는 기계가 가지고있는 물리적 기억의 양입니다.
다른 팁
메모리 매핑 파일은 32 비트 VM에서 주소 공간이 부족할 수 있습니다. 파일이 작은 덩어리에 매핑되고 바이트 버퍼에 더 이상 도달 할 수없는 경우에도 발생합니다. 그 이유는 GC가 버퍼를 풀기 위해 결코 시작하지 않기 때문입니다.
버그를 참조하십시오 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6417205