[memory heap] Duży plik tekstowy i wątek

0

Cześć!

Mam taki mały problem z dużymi plikami. A właściwie mały plik (10mega) ale dużo linii (około 240 000) odczytuje to w ten sposób (wycinek SwingWorker):

                in = new BufferedReader(new FileReader(file));

                while((line = in.readLine()) != null)
                {
                    boolean state = true;
                    all++;

                    line = line.replace("\"", "");
                    String[] lin = line.split(";");

                    String[] check = logic.impCheckCase(lin[position[1]]);

                    if(check[0].equals("false"))
                    {    state = false; noCaseAdd++; }

                    if(param.get("CN"))
                        if(!logic.impCheckNumber(lin[position[2]]))
                        {    state = false; noNumberAdd++; }
                    
                    if(param.get("CE"))
                        if(logic.impCheckExistsCase(lin[position[1]]))
                        {   state = false;  noAddIfExistsCase++; }
                    

                    if(state)
                        if(logic.impInsert(lin[position[1]], lin[position[2]], check)) //TUTAJ LECI DO BAZY MYSQL
                            add++;

                    progress.setValue(all);
                }

...tyle tylko że po kilku chwilach (gdzieś w okolicach 40 tys linii) wywala mi błąd przepełnienia pamięci Heap Space - czy coś takiego.... jak ugryźć ten problem?

Screen z VM Telemetry:
http://zapodaj.net/441fd8323201.jpg.html
http://zapodaj.net/3e05a97f2ebc.jpg.html

0

Uruchom program z opcjami -Xmx512m i -Xms128m

No i sprawdź czy gdzieś nie siedzą jakies cięzkie obiekty w stylu niezamykanych połączeń.

0
Koziołek napisał(a)

Uruchom program z opcjami -Xmx512m i -Xms128m

No i sprawdź czy gdzieś nie siedzą jakies cięzkie obiekty w stylu niezamykanych połączeń.

ustawiam na 128 i...

Error occurred during initialization of VM
Incompatible minimum and maximum heap sizes specified

poza tym to rozwiazanie na krótką metę - kiedyś i to się przepełni :/

daje całą klasę bo wydaje mi się że nie ma takich obiektów:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package pl.maksi.nitro.countObOnw.logic;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.HashMap;
import javax.swing.JComponent;
import javax.swing.JProgressBar;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;

/**
 *
 * @author Lusaczek
 */
public class THRImport extends SwingWorker
{
    private String file;
    private HashMap<String, java.lang.Boolean> param;
    private JTextArea console;
    private JProgressBar progress;
    private CountingApp logic;
    private JComponent[] components;

    public void setProgress(JProgressBar progress) {
        this.progress = progress;
    }

    public void setComponents(JComponent[] components) {
        this.components = components;
    }

    public void setLogic(CountingApp logic) {
        this.logic = logic;
    }

    public void setConsole(JTextArea console) {
        this.console = console;
    }

    public void setFile(String file) {
        this.file = file;
    }

    public void setParam(HashMap<String, Boolean> param) {
        this.param = param;
    }

////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    private void setEnable(boolean ena)
    {
        for(int i = 0; i<components.length; i++)
            components[i].setEnabled(ena);
    }


    @Override
    protected Object doInBackground() throws Exception
    {
        BufferedReader in = null;
        String line = "";
        int all = 0, add = 0, noCaseAdd = 0, noNumberAdd = 0, noAddIfExistsCase = 0;

        //Czyszczenie konsoli
        console.setText("");


        this.setEnable(false);
        console.append("Pola formularza zostały dezaktywowane\n");



        try{



                console.append("Analizuje plik...\n");
                int[] position = logic.impAnalisString(file);

                if(position[0] == 0)
                {   console.append("Bład w walidacji pliku. Struktura niepoprawna!\n");
                    this.cancel(true);
                }
                else
                {
                    if(param.get("SD"))
                        console.append(" - znaleziono spraw: "+position[3]+"\n\n");

                    progress.setMaximum(position[3]);
                }


                
                console.append("\nRozpoczynam import...\n");
                in = new BufferedReader(new FileReader(file));

                while((line = in.readLine()) != null)
                {
                    boolean state = true;
                    all++;

                    line = line.replace("\"", "");
                    String[] lin = line.split(";");

                    String[] check = logic.impCheckCase(lin[position[1]]);

                    if(check[0].equals("false"))
                    {    state = false; noCaseAdd++; }

                    if(param.get("CN"))
                        if(!logic.impCheckNumber(lin[position[2]]))
                        {    state = false; noNumberAdd++; }
                    
                    if(param.get("CE"))
                        if(logic.impCheckExistsCase(lin[position[1]]))
                        {   state = false;  noAddIfExistsCase++; }
                    

                    if(state)
                        if(logic.impInsert(lin[position[1]], lin[position[2]], check))
                            add++;

                    progress.setValue(all);
                }





        }
        finally
        {
            if(in != null) in.close();
        }



        
        console.append("Zakończono przetważać plik. Podsumowanie:\n");
        console.append(" - zaimportowano: "+add+"\n");
        console.append(" - błędy walidacji: "+(noCaseAdd+noNumberAdd+noAddIfExistsCase)+"\n");
        if(param.get("SD"))
        {   console.append("   - w tym błąd sprawy: "+noCaseAdd+"\n");
            console.append("   - w tym błędy numeru beneficjenta: "+noNumberAdd+"\n");
            console.append("   - w tym powtarzające się numery spraw: "+noAddIfExistsCase+"\n\n");
        }


        return true;
    }

    @Override
    protected void done()
    {
        
        //aktywacja przyciskow
        console.append("Pola formularza zostały aktywowane\n");
        this.setEnable(true);
    }





}

...myślałem że to kwestia transakcji w mysql, jak miałem transakcje na ON to przepełnienie pamięci prawie natychmiast następowało, a teraz to kilka chwil później
...hmm tak sie zastanawiam czy faktycznie to nie jest wina mysql

0

faktycznie to wina samego dodawania do BAZY, no ale jak to dodać? jakoś trzeba
taki prosty kod mam:

    public boolean impInsert(String cas, String num, String[] add)
    {   boolean re = true;
        int type = 0;
        if(add[1].equals("OB"))
            type = 1;
        else if(add[1].equals("ONW"))
            type = 2;

        String sql = "INSERT INTO counting_case (office, type, name, number, dateImport) VALUES ("+add[2]+", "+type+", '"+cas+"', '"+num+"', NOW())";
        //System.out.println(sql);
        re = sess.conDB.insert(sql);


        return re;
    }
0

Skorzystaj z pojedynczego połączenia. Zaimplementuj dostęp do bazy jako singleton:

public class DBConnectionFactory{
    private static Connection connection;

    public static Connection getConnection(){
       if(connection == null || connection.isColsed()){
            createNewConnection();
       }
       return connection;
    }
    
    public static void createNewConnection(){
      // tworzysz połączenie i przypisujesz je do connection
    }



}

co do opcji to SOA nr1. U mnie działa. Xms MUSI być mniejsze niż Xmx sprawdź jak wpisujesz.

0
Koziołek napisał(a)

Skorzystaj z pojedynczego połączenia. Zaimplementuj dostęp do bazy jako singleton:

dzieki za zainteresowaniem tematem.... więc zrobiłem tak:

Factory.java

package pl.maksi.nitro.supply.database;

import com.mysql.jdbc.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Lusaczek
 */
public class Factory
{
    protected static Connection connection = null;
    private static String error;
    
    public static Connection getConnection()
    {
        try {
            if (connection == null || connection.isClosed()) 
            {
                createNewConnection();
            }
            return connection;
        } catch (SQLException ex) {
            Logger.getLogger(Factory.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        }
    }

    static void close() throws SQLException 
    {
        if( connection != null)
            connection.close();
    }
   
    private static void createNewConnection()
    {      try
        {
             Class.forName("org.gjt.mm.mysql.Driver");

             //outp.println("Połączenie nawiązane!");
        }
      catch(ClassNotFoundException e)
        {
           error = "Błąd przy ładowaniu sterownika bazy: " + e;
            //System.exit(-1);
        }

      try
        {

            String adr = java.util.ResourceBundle.getBundle("pl/maksi/nitro/propaties/general").getString("DBhost");
            String port = java.util.ResourceBundle.getBundle("pl/maksi/nitro/propaties/general").getString("DBport");
            String base = java.util.ResourceBundle.getBundle("pl/maksi/nitro/propaties/general").getString("DBbase");
            String username = java.util.ResourceBundle.getBundle("pl/maksi/nitro/propaties/general").getString("DBuser");
            String password = java.util.ResourceBundle.getBundle("pl/maksi/nitro/propaties/general").getString("DBpass");
            //System.out.print("jdbc:mysql://"+ adr +":"+ port +"/"+ base +"");
            connection = (Connection) DriverManager.getConnection("jdbc:mysql://"+ adr +":"+ port +"/"+ base +"?useUnicode=true&characterEncoding=UTF-8&useOldUTF8Behavior=true", username, password);

        }
      catch(SQLException e)
        {
            error = "Nie można nawiązać połączenia z bazą: " + e;
            //System.exit(-1);
        }
    
    }


}

Maintenance.java

public class Maintenance 
{
    private String error;
    private int insertId;
    private boolean transaction;

    private Connection getCon()
    {
        return Factory.getConnection();
    }

    
    public ResultSet result(String sql)
    { ResultSet result = null;
     
      try
        {
            Statement statement = (Statement) getCon().createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
            ResultSet rs = statement.executeQuery(sql);
            result = rs;
        }
      catch(SQLException e)
        { this.error = "Błąd podczas przetwarzania danych: " + e; }
      catch(Exception e)
        { this.error = "Błąd WYNIKU - ogólny: " + e; }
      
      return result;
    }

    
    public boolean insert(String sql)
    {   boolean re = false;

        try
        {
            Statement statement = (Statement) getCon().createStatement();
            if(statement.executeUpdate(sql)==1)
            {
                re = true;
                insertId = setInsertId();
            }

        }
      catch(SQLException e)
        { this.rollback(); this.error = "Błąd podczas MYSQL:insert: " + e; }
      catch(Exception e)
        { this.rollback(); this.error = "Błąd WYNIKU - ogólny: " + e; }



        return re;
    }

//itd...

}

poczym modyfikuje dodawanie rekordu...

    public boolean impInsert(String cas, String num, String[] add)
    {   boolean re = true;
        int type = 0;
        if(add[1].equals("OB"))
            type = 1;
        else if(add[1].equals("ONW"))
            type = 2;


        Maintenance m = new Maintenance();

        String sql = "INSERT INTO counting_case (office, type, name, number, dateImport) VALUES ("+add[2]+", "+type+", '"+cas+"', '"+num+"', NOW())";
         m.insert(sql);

        return re;
    }

efektem... tego jest....

http://zapodaj.net/b361429c0ef8.jpg.html
http://zapodaj.net/b361429c0ef8.jpg.html
http://zapodaj.net/7174fb8ea2b0.jpg.html

czyli nie jest źle efekt jest widoczny... tyle tylko że strasznie wolno a dysk prawie czerwony tak sie kreci ...zerknij prosze czy dobrze zrozumialem singleton i czy da sie to zoptymalizowac?

0

Kod jest dobrze co do kręcenia się dysku to można by to jeszcze trochę zoptymalizować, bo chociażby dla każdej linii z pliku wykonujesz insert synchronicznie. warto by pomyśleć o jakimś asynchronicznym wywołaniu insertu. Wiem, że to może być trudne do napisania, ale:

  1. JVM jest uzależniony od systemu w kwestii obsługi plików.
  2. Instert może być długi np. przy blokadzie lub jak baza stwierdzi że twoje połączenie musi poczekać co przy synchronicznym wywołaniu blokuje dysk...
  3. Bazy danych - JDBC jest to jakiś taki zalążek który wypłodziłem z pół roku temu dość po łebkach i nieaktualny, ale warto przyjrzeć się mu w kwestii zarządzania połączeniami i obiektami. Może warto stworzyć obiekt reprezentujacy pojedynczą linię pliku i na nim wykonywać inserty oddzielnie od wczytywania?
0
Koziołek napisał(a)

warto by pomyśleć o jakimś asynchronicznym wywołaniu insertu. Wiem, że to może być trudne do napisania,

co do tej asynchroniczności to jest możliowść łatwego osiągniecia tego - tak jakby. jest taka metoda dla Statment - addBatch() a potem executeBatch() ...no ale nie próbowałem tego ponieważ wydaje mi się że takie pakowanie do pamięci niczego dobrego nie przyniesie.

Co do twojego artykułu - szkoda że nie dokończyłeś. Nie do końca wiem jak wykorzystać twoją wiedze co do implementacji tego u mnie w aplikacji.
Ponadto mam nowy problem....

ResultSet zwraca mi ponad 200tys rekordow i w tym momencie 'heap' leci na łeb na szyje w ciągu kilku sekund. Czy jest na to sposób czy mam to obejść limitując wyniki?

A tak w ogóle... to jak sie ma wzorzec projektowy do praktyki... jakim wzorcem sie kierować podczas pisania aplikacji? ...i może polecasz jakąś publikację papierowa/elektroniczna? Coś o wzorcach - tak żeby w SPACJA końcu zacząć pisać jak sie należy ;)

0

Hm... 200k rezultatów... Jeżeli wykorzystujesz JDBC to można spokojnie zrobić na końcu zapytania "LIMIT 100 OFFSET ?" gdzie ? podstawiasz odpowiednie parametry do listowania. Swoją drogą to poszukaj w tematach phpowych, bo oni tam dość często przerabiają stronicowanie wyników.

Jeżeli używasz JPA to masz setMaxResult i setFirstResult.

0

heh stroniccowanie odpada - ja te 200k nie wyswietlam tylko pracuaje na nich ...te 200k to po prostu wywalanie wszystkich rekordow a w petli dopiero limituje - w petli dopiero poniewaz nie wiem ile i jakie rekordy bede potrzebował... potem w zaleznosci od parametru zapisuje w pliku A lub w pliku B lub X... itd, albo w pliku A, ale jesli jest gorna granica rekordow w pliku to tez sprawdzam

0

od kąt zacząłem używać tego połączenia (przez Singleton) wyskakuje mi błąd

com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

Last packet sent to the server was 0 ms ago.

zdecydowanie pojawia się gdy mam mnóstwo insertów jeden po drugim :/

jakieś pomysły :?

1 użytkowników online, w tym zalogowanych: 0, gości: 1