2011-04-19 6 views
14

HashMap의 서브 클래스 인 내부 클래스를 사용하고 있습니다. 나는 String을 키로, double[]을 값으로 사용한다. 나는 double[] 당 약 200 배의 복식을 저장합니다. 키, 포인터 및 복식을 저장하려면 약 700   MB를 사용해야합니다. 그러나 메모리 분석 결과 두 개 이상 필요하다는 것을 알 수 있습니다 (2보다 약간 작음   GB).Java에서 직렬화 가능, 복제 가능 및 메모리 사용

TIJmp (프로파일 링 도구)을 사용하여 전체 메모리의 거의 절반을 사용하는 char[]이 있음을 확인했습니다. TIJmp는 char[]SerializableCloneable에서 온다고 말했습니다. 그 값은 글꼴 목록과 메시지 및 단일 문자에 대한 기본 경로에서 다양합니다.

JVM에서 Serializable의 정확한 동작은 무엇입니까? 항상 "영구"복사본을 유지하므로 내 메모리 사용 공간이 두 배가됩니까? JVM을 메모리 돼지로 바꾸지 않고 런타임에 객체의 바이너리 복사본을 작성하려면 어떻게해야합니까?

추 신 : 메모리 사용량이 가장 많이 증가하는 방식은 다음과 같습니다. 이 파일은 라인 당 약 229,000 개의 라인과 202 개의 필드를 가지고 있습니다.

public void readThetas(String filename) throws Exception 
{ 
    long t1 = System.currentTimeMillis(); 
    documents = new HashMapX<String,double[]>(); //Document names to indices. 
    Scanner s = new Scanner(new File(filename)); 
    int docIndex = 0; 
    if (s.hasNextLine()) 
     System.out.println(s.nextLine()); // Consume useless first line :) 
    while(s.hasNextLine()) 
    { 
     String[] fields = s.nextLine().split("\\s+"); 
     String docName = fields[1]; 
     numTopics = fields.length/2-1; 
     double[] thetas = new double[numTopics]; 
     for (int i=2;i<numTopics;i=i+2) 
      thetas[Integer.valueOf(fields[i].trim())] = Double.valueOf(fields[i+1].trim()); 
     documents.put(docName,thetas); 
     docIndex++; 
     if (docIndex%10000==0) 
      System.out.print("*"); //progress bar ;) 
    } 
    s.close(); 
    long t2 = System.currentTimeMillis(); 
    System.out.println("\nRead file in "+ (t2-t1) +" ms"); 
} 

아!와 HashMapX는 다음과 같이 선언 된 내부 클래스입니다 :

public static class HashMapX< K, V> extends HashMap<K,V> { 
    public V get(Object key, V altVal) { 
     if (this.containsKey(key)) 
      return this.get(key); 
     else 
      return altVal; 
    } 
} 
+0

일부 코드 샘플을 표시 할 수 있습니까? – axtavt

+1

Serializable이 메모리 풋 프린트를 증가시키는 것을 보여주는 테스트를 게시하십시오. 많은 RAM을 보여주는 코드를 게시 할 수 있다면 Map 이 도움이 될 것입니다. –

+0

당신의 진술을 이해할 수 있는지 보도록하겠습니다. Serializable 클래스를 선언하면 인스턴스가 차지하는 크기가 일시적 일 때보 다 더 커집니다. –

답변

4

그래서 답변을 찾았습니다. 그것은 내 코드에서 메모리 누수입니다. Serializable 또는 Cloneable과 관련이 없습니다.

이 코드는 파일을 구문 분석하려고합니다. 각 행에는 추출하려는 값 세트가 들어 있습니다. 그런 다음이 값 중 일부를 HashMapX 또는 다른 구조에 저장합니다.

 String[] fields = s.nextLine().split("\\s+"); 
     String docName = fields[1]; 

나는 여기 전파 :

문제의 핵심은 여기에서 일어나는 것들 docName 같은 배열 (필드)의 요소에 대한 참조하고 있다는 것이다

 documents.put(docName,thetas); 

나는 프로그램의 수명에 대한 참조를 유지하고있다 (글로벌 HashMap 문서에 저장). 해당 참조를 유지하는 한 전체 String [] 필드는 가비지 수집 될 수 없습니다. 솔루션 :

 String docName = new String(fields[1]); // A copy, not a reference. 

따라서 개체를 복사하고 배열 요소에 대한 참조를 해제하십시오. 이런 방식으로 모든 필드를 처리하면 가비지 수집기가 배열에서 사용하는 메모리를 확보 할 수 있습니다.

분할을 사용하여 큰 텍스트 파일을 구문 분석하고 일부 필드를 전역 변수에 저장하는 모든 사용자에게 유용하게되기를 바랍니다.

의견에 감사드립니다. 그들은 올바른 방향으로 나를 인도했습니다.

5

이 당신의 모든 질문을 해결하지 않을 수 있습니다,하지만 직렬화는 크게 메모리 사용을 증가시킬 수있는 방법입니다 http://java.sun.com/javase/technologies/core/basic/serializationFAQ.jsp#OutOfMemoryError .

간단히 말해서 ObjectOutputStream을 열어두면 명시 적으로 해당 reset() 메서드를 호출하지 않으면 해당 개체에 쓰여진 개체가 가비지 수집되지 않을 수 있습니다.

+1

원래 게시물은 클래스를 직렬화 가능하게 만들고 개발자가 직렬화 할 수없는 더미 클래스에 대한 테스트를 수행하고 결정된 메모리 풋 프린트가 더 작기 때문에 문제의 객체가 실제로 직렬화되는 경우 좋은 리드입니다. 그러나이 평가가 어떻게 수행되었는지는 모르겠다.) 그러나 이것이 사실 인 경우 루트 케이스는 다른 것이어야한다. 솔직히, 나는 당신의 설명이 지금까지 가장 논리적 인 것이라고 믿기를 너무 좋아합니다. –

관련 문제