2010-01-14 4 views
13

Windows Mobile 디바이스에서 실행되는 Java로 애플리케이션을 개발 중입니다. 이것을 달성하기 위해 우리는 Esmertec JBed JVM을 사용했습니다. JBoss JVM은 완벽하지는 않지만 지금 당장은 당황합니다. 최근 우리는 고객으로부터 OutOfMemoryErrors에 대한 불만을 제기하고 있습니다. 많은 것들을 가지고 놀고 난 후에 그 장치에는 충분한 여유 메모리 (약 4MB)가 있음을 발견했습니다.Java에서 많은 배열을 할당 할 때 메모리 단편화를 피하십시오.

OutOfMemoryErrors는 항상 코드의 같은 지점에서 발생하며 일부 문자를 추가하기 위해 StringBuffer를 확장 할 때 발생합니다. 이 영역 주위에 로깅을 추가 한 후, StringBuffer에 약 290000 자의 용량을 가진 것으로 나타났습니다. 내부 문자 배열의 확장 전략은 단순히 크기를 두 배로 늘려서 배열을 할당하려고 시도하는 것입니다. 약 580000 자. 나는이 시간에도 메모리 사용량을 출력했는데, 총 사용 가능한 메모리가 시간당 약 12MB까지 증가했기 때문에 3.8MB (메모리 사용량은 약 3.8MB)를 사용하고 있다는 것을 알았습니다. 따라서 확장 할 공간이 충분합니다. 따라서 응용 프로그램에서 OutOfMemoryError를보고하는 시점이 있습니다. OutOfMemoryError는 아직 무료로 제공되는 양을 감안할 때 많은 의미가 없습니다.

나는 지금까지 응용 프로그램의 작동에 대해 생각하기 시작했습니다. 기본적으로 나는 작은 XML Sax Parser 인 MinML을 사용하여 XML 파일을 파싱하고있다. XML의 필드 중 하나에는 약 300,000 자의 문자가 있습니다. 파서는 디스크에서 데이터를 스트리밍하며 기본적으로 한 번에 256 자만로드합니다. 그래서 문제의 필드에 도달하면 파서는 1000 번 이상 처리기의 'characters()'메서드를 호출합니다. 매번 256자를 보유하는 새로운 char []를 생성합니다. 핸들러는 단순히 이러한 문자를 StringBuffer에 추가합니다. StringBuffer의 기본 초기 크기는 12이며, 버퍼에 문자가 추가 될 때마다 새로운 char []를 만들 때마다 여러 번 커야합니다.

내 가정은 이전 char [] s가 가비지 수집 될 수 있기 때문에 충분한 여유 메모리가 있지만 어쩌면 새로운 배열에 적합 할만큼 큰 메모리 블록이 없을 수도 있습니다. 할당하십시오. JVM은 힙 크기를 확장 할만큼 똑똑하지 않을 수도 있습니다. 왜냐하면 바보 같기 때문에 필요하지 않다고 생각합니다. 여유 메모리가 충분하기 때문입니다.

내 질문은 : 누구든지이 JVM에 대한 경험이 있고 결론적으로 메모리 할당에 대한 내 가정을 확인하거나 반증 할 수 있습니까? 또한, 메모리를 조각화하지 않도록 배열의 할당을 유도하는 방법에 대한 아이디어가 있습니다 (제 가정가 맞다고 가정).

참고 : 물건은 이미 시도했다 :

  • 본인은 StringBuffer와의 초기 배열 크기를 증가하고 많은 배열을 만들 필요가 없습니다 것이다 그래야 내가 파서의 읽기 크기를 increaed.
  • StringBuffer의 확장 전략을 변경하여 특정 크기 임계 값에 도달하면 100 %가 아닌 25 % 만 확장 할 수 있도록 변경했습니다.

이 두 가지를 모두하는 것이 도움이되었지만 XML 데이터 크기를 늘리면 OutOfMemoryErrors가 상당히 낮은 크기 (약 350kb)로 나옵니다.

추가 할 사항 :이 테스트는 모두 해당 JVM을 사용하는 장치에서 수행되었습니다. Java SE 1.2 JVM을 사용하여 데스크탑에서 동일한 코드를 실행해도 문제가 발생하지 않거나 데이터 크기가 약 4MB에 도달 할 때까지 문제가 발생하지 않습니다.

편집 :

조금 나는 10M에 XMS를 설정입니다 도움이되었습니다 난 그냥 시도 또 다른 한가지. 따라서 JVM이 힙을 확장하지 않는 문제를 극복하고 오류가 발생하기 전에 더 많은 데이터를 처리 할 수있게되었습니다.

답변

2

내 자신의 질문을 업데이트하기 위해 최선의 해결책은 최소 힙 크기를 설정하는 것이었다. (나는 10M로 설정했다.)이것은 JVM이 힙을 확장할지 여부를 결정할 필요가 없으므로 충분한 여유 공간이 있어야하지만 OutOfMemoryError로 절대로 죽지 않는다는 것을 의미합니다. 지금까지 테스트를 통해 우리는 오류없이 구문 분석 한 데이터의 양을 3 배로 늘릴 수 있었고 실제로 필요하다면 더 나아갈 수있었습니다.

이것은 기존 고객을 행복하게 유지하는 빠른 해결책을위한 약간의 해킹이지만 다른 JVM을 찾고 있습니다. JVM이이 scneario를 더 잘 처리하면 업데이트로 다시보고 할 것입니다.

0

나는 많은 메모리를 가지고 있지만 많은 수의 참조 객체를 생성하고 있다고 생각합니다. 자세한 내용은이 문서를 참조하십시오 : https://web.archive.org/web/1/http://articles.techrepublic%2ecom%2ecom/5100-10878_11-1049545.html?tag=rbxccnbtr1.

+0

정말인가요? 이 기사는 객체를 가비지 수집하는 방법을 설명합니다. –

+0

참조 개체가 없습니다. 내가 말했듯이, JVM이 많은 여유 메모리를보고하기 때문에 가비지 수집되지 않는 객체에 문제가 있다고 생각하지 않습니다. 여유 메모리가 어디에 있는지 질문입니다. 조각난거야? JVM이 새 배열을 할당 할 수없는 이유는 무엇입니까? – DaveJohnston

0

이 StringBuffers가 MinML 내부에 할당되는지 확실하지 않습니다. 그렇다면 소스가 있다고 가정합니다. 그렇게하면 문자열을 스캔 할 때 문자열이 특정 길이 (예 : 10000 바이트)에 도달하면 문자열의 정확한 길이를 결정하고 해당 크기에 버퍼를 다시 할당 할 수 있습니다 . 이것은 추한 것이지만, 메모리를 절약 할 것입니다. (당신이 잠재적으로 많은 재 할당을 저장하고 있기 때문에 심지어는 lookaheads 일을하지 않는 것보다 더 빠를 수 있습니다.)

당신이 MinML 소스에 액세스 할 수 없습니다 할 경우, 잘 모르겠어요 무엇을 StringBuffer의 수명은 XML 문서에 상대적입니다. 그러나이 제안은 여전히 ​​효과가 있을지 모르지만 디스크에서 XML을 얻는다면 문자열의 크기를 얻기 위해 SAX 파서를 사용하여 미리 구문 분석 할 수 있습니다. 필드를 할당하고 이에 따라 StingBuffers를 할당합니까?

+0

StringBuffers는 SaxParser (이 경우 MinML)의 Handler 개체에 할당됩니다. 따라서 문제의 핸들러는 StringBuffer를 할당하고 나서 characters() 메서드가 호출 될 때마다 더 많은 데이터가 추가됩니다. 문자열을 스캔하지 않고 파일에서 모두 스트리밍되므로 두 번째 제안에서 말한 것처럼 파일을 두 번 구문 분석하지 않는 한 최종 문자열의 크기를 미리 확인할 수 없습니다. 그러나 당신이 말했듯이 그것은 추악하고 시간 소모적입니다. – DaveJohnston

+0

미운, 그렇습니다. 그러나 현재의 방법에 많은 재 할당이 필요한 경우 특히 예상보다 빠를 수도 있습니다. –

0

장치에서 힙 덤프를 가져올 수 있습니까?

힙 덤프가 있고 호환되는 형식 인 경우 일부 Java 메모리 분석기는 인접한 메모리 블록의 크기에 대한 정보를 제공합니다. IBM 힙 분석기 http://www.alphaworks.ibm.com/tech/heapanalyzer에서이 기능을보고 기억하지만 최신 Eclipse 메모리 분석기도 확인하십시오. http://www.eclipse.org/mat/

XML 파일을 수정할 가능성이 있다면 아마도 가장 빠른 방법 일 것입니다. Java에서의 XML 구문 분석은 항상 메모리 집약적이며 300K는 단일 필드에서 상당히 많이 발생합니다. 대신이 필드를 별도의 비 xml 파일로 분리 할 수 ​​있습니다.

+0

나는 힙 덤프를 얻을 수 있을지 매우 의심 스럽다. JVM은 당신이 할 수있는 일이 매우 제한적이거나 적어도 문서화가 잘되어 있지 않기 때문에 어떻게해야할지 모른다. XML은 서버가 반환하는 검색 결과 집합이므로 최후의 수단으로 고려해야 할 가능성이 있습니다. 변경은 서버 구조를 변경하여 JVM의 문제와 같은 문제를 해결하는 것입니다. 그것이 문제가되지 않는다면 JVM이 제대로 작동 할 수있는 방법을 찾게 될 것입니다. – DaveJohnston

1

JVM에 대해 알고있는 바에 따르면 단편화는 결코 문제가되지 않아야합니다. 은 해결할입니다. 분할의 여지가 없든 가비지 수집기가 실행되어야하는지에 대한 할당 여유가 없으면 GC는 일반적으로 조각화 문제를 해결하기 위해 데이터를 압축합니다.

강조 표시 - 이후에 "메모리 부족"오류 만 발생합니다. GC가 실행되었지만 여전히 충분한 메모리를 확보 할 수 없습니다.

나는 대신 실행중인 특정 JVM에 대한 옵션을 더 파헤 치려고합니다. 예를 들어 "복사"가비지 수집기는 한 번에 사용 가능한 메모리의 절반 만 사용하므로 다른 것을 사용하도록 VM을 변경하면 메모리의 절반을 확보 할 수 있습니다.

VM에 간단한 GC 복사를 사용하는 것은 아닙니다. VM 수준에서이 문제를 조사하는 것이 좋습니다.

+0

내가 사용하고있는 JVM에 대한 지원은 유감스럽게도 (존재하지 않는 사람은 거의 없다) (Esmertec JBed CDC에 대한 지원을받을 수있는 좋은 곳이 없다면 ??). GC 옵션을 변경하기위한 표준 명령 행 옵션은 무엇입니까? – DaveJohnston

+0

@DaveJohnston : 인기있는 JVM에 대한 설명서를 확인하고 자신의 동작이 동일하게 이루어지기를 바랍니다. Java VM 사양으로 정의되고있는 표준은 없습니다 (실제로, 명시 적으로 말하면 : "런타임 데이터 영역의 메모리 레이아웃, 사용 된 가비지 콜렉션 알고리즘 [...]은 구현 자의 재량에 맡겨져 있습니다"). – Oak

2

어쩌면 VTD 조명을 시도 할 수 있습니다. 그것은 SAX보다 더 많은 메모리 효율적인 것 같습니다. (나는 거대한 변화라는 것을 알고있다.)