2012-06-14 3 views
2

opencsv를 사용하고 정보를 ArrayList로 읽는 CSV 파일, 특히 POI 파일을 구문 분석하고 있습니다. 메모리에 정보를 캐시해야하므로 사용자가 버튼을 눌렀을 때 모든 POI를 확인하고 맵보기의 현재 범위 내에 있는지 확인합니다. 일부 POI 파일은 10K - 60K 행을 가질 수 있습니다. 내 응용 프로그램 강제 종료하기 전에 약 50K 행을 읽을 수 있으므로 다른 것들을 위해 메모리를 남겨 둘 30K의 한계를 설정합니다. 내 문제는 제가 Arraylists I trimToSize()를 지우고 다른 파일을로드하려고 할 때 ArrayLists를 새로운 ArrayList로 선언했지만 GC가 메모리에서 이전 데이터를 절대로 릴리스하지 않았습니다. 내가 그들을 지우고 새 파일을 읽을 수는 있지만 GC가 메모리를 확보하지 못하게하는 것입니다. 나는 프로그래밍, IT 또는 CS에 대한 교육을받지 못했다. 이것은 Java/Android로 작성 또는 작성한 첫 번째 앱입니다. 나는 왜이 메모리 누출이 있었는지 알아 내려고 약 6 일 동안 일하고, 읽고 공부했습니다. 어떤 도움을 주셔서 감사 드리며 내가 코드를 최적화 할 수있는 방법에 대한 제안은 내가 완전한 멍청이이기 때문에 높이 평가 될 것입니다. 또한 아래 코드는 파일을 메모리로 읽는 방법만을 보여줍니다. 당신은 구글이 어떻게 작동하는지에 대한 문서를보기 위해 opencsv 할 수 있고, 다른 것을 알 필요가 있다면 알려주고 그것을 게시 할 수 있습니다.HashMap 및 ArrayList를 사용하는 Android OutOfMemoryError

미리 감사드립니다.

public class MainActivity extends MapActivity implements LocationListener { 
    private static MapView mapView; 
    int counter = 0; 
    private ArrayList<String> arrLat = new ArrayList<String>(); 
    private ArrayList<String> arrLong = new ArrayList<String>(); 
    private ArrayList<String> arrName = new ArrayList<String>(); 
    private ArrayList<String> arrInfo = new ArrayList<String>(); 
    private ArrayList<Boolean> arrCheck = new ArrayList<Boolean>(); 
    private ProgressDialog progressDialog; 

@Override 
public void onCreate(Bundle savedInstanceState) { 

    super.onCreate(savedInstanceState); 
    // main.xml contains a MapView 
    setContentView(R.layout.main); 

    //Gets file name from ListOfFiles Activity Class 
      Bundle extras = getIntent().getExtras(); 
      if (extras != null) { 
       boolean callreadPOIFile = extras.getBoolean("callreadPOIFile"); 
       if(callreadPOIFile) { 
        String filePath = extras.getString("filePath"); 
        readPOIFileInThread(filePath); 

       }else{ 
        // Show user alert box      
       } 

      } 
} 

public void readPOIFileInThread(String filePath) { 


    progressDialog = ProgressDialog.show(this, "", "LOADING:\n" + filePath + "\nPLEASE WAIT..."); 
    final String finalFilePath = filePath; 

    new Thread(new Runnable(){ 
     public void run(){ 
      try{ 
       readPOIFile(finalFilePath); 
      }catch(Exception e){ 
       runOnUiThread(new Runnable() { 
        public void run() { 
       Toast.makeText(getApplicationContext(), "Exception, readPOIFileInThread", Toast.LENGTH_SHORT).show(); 
       //progressDialog.dismiss(); 
        } 
       }); 
      } 

      progressDialog.dismiss(); 

     } 
    }).start(); 

}  


//Parse and load POI CSV File 
public void readPOIFile(String filePath){ 


    arrLat.clear(); 
    arrLong.clear(); 
    arrName.clear(); 
    arrInfo.clear(); 
    arrCheck.clear(); 

    arrLat.trimToSize(); 
    arrLong.trimToSize(); 
    arrName.trimToSize(); 
    arrInfo.trimToSize(); 
    arrCheck.trimToSize(); 

      //arrLat = null; 
      //arrLong = null; 
      //arrName = null; 
      //arrInfo = null; 
      //arrCheck = null; 

      //arrLat = new ArrayList<String>(); 
      //arrLong = new ArrayList<String>(); 
      //arrName = new ArrayList<String>(); 
      //arrInfo = new ArrayList<String>(); 
      //arrCheck = new ArrayList<Boolean>(); 

    System.out.println(arrLat.isEmpty()); 

    String lat = null; 
    String lng = null; 
    Double dLat; 
    Double dLng; 
    int lati; 
    int lngi; 
    String name = null; 
    String info = null; 

    CSVReader reader = null; 
    //System.out.println(filePath); 
    try { 
     reader = new CSVReader(new FileReader(filePath)); 
    } catch (FileNotFoundException e) { 
     // prepare the alert box 
     AlertDialog.Builder alertbox = new AlertDialog.Builder(this); 

     // set the message to display 
     alertbox.setMessage("There was an error reading file: " + filePath 
       + "\n Please check the file format and try again."); 

     // add a neutral button to the alert box and assign a click listener 
     alertbox.setNeutralButton("Ok", new DialogInterface.OnClickListener() { 

      // click listener on the alert box 
      public void onClick(DialogInterface arg0, int arg1) { 
       // the button was clicked 
       //Toast.makeText(getApplicationContext(), "OK button clicked", Toast.LENGTH_SHORT).show(); 
      } 
     }); 

     // show it 
     alertbox.show(); 
     e.printStackTrace(); 
    } 
    String [] nextLine = null; 
    int count = 0; 
    try { 
     while ((nextLine = reader.readNext()) != null) { 
      // nextLine[] is an array of values from the line 
      //System.out.println(nextLine[0]+ "\n" + nextLine[1]+ "\n" + nextLine[2]+ "\n" + nextLine[3] + "\n"); 

      try { 
       lng = nextLine[0]; 

      } catch (Exception e) { 
       lng = Integer.toString(1); 
      } 

      try { 
       lat = nextLine[1]; 

      } catch (Exception e) { 
       lat = Integer.toString(1); 
      } 
      try { 
       name = nextLine[2]; 

      } catch (Exception e) { 
       name = "No Name..."; 
      } 
      try { 
       info = nextLine[3]; 
      } catch (Exception e) { 

       info = "No Info..."; 
      } 
      //convert lat and long to double 
      try{ 
       dLat = Double.parseDouble(lat); 
       dLng = Double.parseDouble(lng); 
      }catch(Exception e){ 

       System.out.println("error converting lat long to Double at row: " + count); 
       break; 

      } 
      //convert lat lng to int 
      lati = (int)(dLat * 1E6); 
      lngi = (int)(dLng * 1E6); 

      //add line to ArrayLists 
      try{ 
      arrLat.add(Integer.toString(lati)); 
      arrLong.add(Integer.toString(lngi)); 
      arrName.add(name); 
      arrInfo.add(info); 
      arrCheck.add(false); 
      }catch (Exception e){ 

       runOnUiThread(new Runnable() { 
        public void run() { 
         //Toast.makeText(getApplicationContext(), "Error reading. Please check the file. ", Toast.LENGTH_SHORT).show(); 
         System.out.println("Error reading file."); 

        } 
       }); 
      } 
      count++; 
      if(count == 10000 || count == 20000){ 
       final int showcount = count; 
       runOnUiThread(new Runnable() { 
        public void run() { 
         Toast.makeText(getApplicationContext(), showcount + " POI's loaded", 
           Toast.LENGTH_LONG).show();    
        } 
       }); 
      } 

      if(count == 30000) 
       break; 

      System.out.println(count); 
     } 
     final String toastFilePath = filePath; 
     final int toastcount = count; 

     runOnUiThread(new Runnable() { 
      public void run() { 
       if(toastcount > 0){ 
        Toast.makeText(getApplicationContext(), "File: " + toastFilePath + " read... \n" 
          + toastcount + " point(s) were loaded...", 
          Toast.LENGTH_LONG).show(); 
       }else{ 
        Toast.makeText(getApplicationContext(), "INVALIDE FILE!\nFile: " + toastFilePath + " read... \n" 
          + toastcount + " points.", 
          Toast.LENGTH_LONG).show(); 
       } 
      } 
     }); 



    } catch (IOException e) { 

     e.printStackTrace(); 
    } 

} 

는 고정 :

나는 마침내 내 문제를 발견! 활동 라이프 사이클을 연구 한 후 필자는 필자의 목록 활동으로 파일을 선택하고 캐싱 할 때마다 새 인스턴스가 생성되어 MainActivity의 새 인스턴스를 작성하고 있음을 발견했습니다. MainActivity를 매니페스트의 singleTop 모드로 설정하고 onNewIntent() 메서드를 일부 코드로 옮겼습니다. 모두 잘되었습니다. 내 응용 프로그램이 잘 작동합니다!

+1

CSV 파일을 데이터베이스 테이블로 읽어 들이지 않고 데이터베이스를 대신 쿼리하는 특별한 이유가 있습니까? – dmon

+0

DB로 읽으려고하지 않았습니다. 나는 시작하기 전에 약간의 연구를했고, 많은 지체를 가지고있는 사람들의 많은 게시물을 발견했다. 나는 최종적으로 내 문제를 발견했다! 활동 라이프 사이클을 연구 한 후 필자는 필자의 목록 활동으로 파일을 선택하고 캐싱 할 때마다 새 인스턴스가 생성되어 MainActivity의 새 인스턴스를 작성하고 있음을 발견했습니다. MainActivity를 매니페스트의 singleTop 모드로 설정하고 onNewIntent() 메서드를 일부 코드로 옮겼습니다. 모두 잘되었습니다. 내 응용 프로그램이 잘 작동합니다! – lentz

답변

1

내 Mapview에 대한 정적 참조와 정적 제거는 getter와 setter를 깨뜨린 것 외에는 아무것도 변경하지 않았습니다. 나는 최종적으로 내 문제를 발견했다! 활동 라이프 사이클을 연구 한 후 필자는 필자의 목록 활동으로 파일을 선택하고 캐싱 할 때마다 새 인스턴스가 생성되어 MainActivity의 새 인스턴스를 작성하고 있음을 발견했습니다. MainActivity를 매니페스트의 singleTop 모드로 설정하고 onNewIntent() 메서드를 일부 코드로 옮겼습니다. 모두 잘되었습니다. 내 응용 프로그램이 잘 작동합니다!

2

부부의 생각은 :

  1. Integer.toString(1)은 문자열 풀을 활용합니다 "1"로 대체 할 수 있습니다.
  2. 모든 값을 String s로 저장하는 대신 프리미티브를 사용해 보셨습니까?
  3. latlng은 위도와 경도를 저장하려고하는 것처럼 들립니다. 아마도 당신은 Double을 사용하고 싶을 것입니다.
  4. ArrayList을 사용하는 대신 정적 크기의 배열을 할당하고 길이를 저장할 수 있습니다.
2

CSVReader 구현에 대해 많이 알지 못하지만 readPOIFile에서 절대로 닫히지 않습니다. 물건에 매달려 있으면 메모리 문제가 발생할 수 있습니다.

4

몇 가지 조언 :

  1. 은 (컨텍스트에 대한 참조가 나 당김, 또는 아무것도) 뷰 객체에 대한 정적 참조를 필요가 없습니다. 그것은 매우 실용적입니다 쉽게 메모리 누수가 발생할 수 있습니다. 이유 : 활동을 떠난 후에도, 모든 활동 영역을 포함하여 남아있는 활동을 참조하는 뷰에 대한 정적 참조가 있습니다. 컬렉션 등). 자세한 내용은 here을 읽으십시오.

  2. 정말로 전체 파일을 읽고 전체 내용을 메모리에 저장해야합니까? 물론 당신에게 쉽고 다른 어떤 것보다 훨씬 빠르지 만, 특히 당신이 그것을 이렇게 사용한다면, 많은 기억을 쉽게 취할 수 있습니다.읽고 싶은 것만, 은 필요한 것만 보관하십시오. & 핸들 메모리를 찾는 방법을 보여주는

  3. 시계 google's 비디오는 정말 문자열의 데이터를 저장해야합니까

  4. 누수? (좌표, 아마?) 또는 포아 컬렉션의 컬렉션에 대해 각각 고유 한 필드 (ID, 이름, 좌표 등)가 있습니까? 자바에서 문자열은 문자의 배열이며 각각 2 바이트 (유니 코드)이므로 메모리에 많은 공간을 사용할 수 있습니다. 예를 들어, 60000 개의 행 시간 80 문자 x 문자 당 2 바이트는 9,600,000 바이트입니다. 거의 10MB입니다. 메모리 사용이 더 엄격해질 필요가 있습니다. 모바일 플랫폼은 메모리 효율성이 최우선 과제 중 하나 (더 나은 작업 전환)에 있다는 것을 기억하십시오.

    Pois 컬렉션을 사용하면 디자인 관점에서 더 뛰어날뿐만 아니라 읽기 쉽고 이해하고 유지 관리 할 수 ​​있습니다. 또한 래퍼 (Integer 대신 int)를 사용하는 대신 기본 공간을 사용하여 공간을 덜 차지합니다.

+0

이 솔루션이 효과가있을 수 있지만 활동 흐름 디자인을 망칠 수도 있습니다. 실제로 mapView를 저장해야하는지 확인하고 대신 필요한 데이터를 사용하십시오. 또한 구성 변경과 같은 활동이 다시 작성되지 않도록하는 매니페스트에 플래그를 설정하십시오. –