Java.lang.OutOfMemoryError: GC overhead limit exceeded

di il
13 risposte

Java.lang.OutOfMemoryError: GC overhead limit exceeded

Salve ragazzi,

ho fatto un programmino java che crea un file excel con i record presenti in una tabella su db oracle.

Fino a circa 40000 record funziona tutto bene, se il numero aumenta ho questo errore:

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded

ho cercato su internet è da quel che ho capito, ma non ne sono sicurissimo, è un problema che indica che per qualche motivo il garbage collector impiega una quantità eccessiva di tempo.

Ho letto che per ovviarlo bisogna impostare
-XX: CMSInitiatingOccupancyFraction = <N>

dove N va da 0 a 100.

Però non ho capito come e dove impostare quel comando.

Qualcuno saprebbe aiutarmi?

13 Risposte

  • Re: Java.lang.OutOfMemoryError: GC overhead limit exceeded

    Il problema e' piu' banale: NON E' un problema del GC, ma del fatto che non c'e' abbastanza memoria per fare quello che serve.

    Poiche' non c'e' abbastanza memoria, il GC inizia a darsi da fare come un matto per cercare di ricuperare tutta la memoria disponibile.
    Ma se questa memoria non c'e', il povero GC inizia a ciclare come un matto, con il risultato che vedi.

    Aumenta la memoria assegnata alla VM con -Xmx4g ad esempio (allochi 4GB) o piu'

    Attento ad usare Java a 64 bit, NON a 32bit.
    E assicurati di avere il S.O a 64 bit.
  • Re: Java.lang.OutOfMemoryError: GC overhead limit exceeded

    CRTVLB ha scritto:


    ho fatto un programmino java che crea un file excel con i record presenti in una tabella su db oracle.

    Fino a circa 40000 record funziona tutto bene, se il numero aumenta ho questo errore:

    Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
    Certamente puoi provare ad aumentare il heap space massimo ma è possibile che il problema si sposti solo un po' più avanti ad un numero maggiore di record es. 200000 (non so ovviamente quanti record devi "potenzialmente" gestire).

    La cosa migliore sarebbe esaminare il sorgente per verificare se c'è un "cattivo" uso degli oggetti. Cosa usi per l'accesso al DB? JDBC nudo e crudo? Un framework di persistenza (es. Hibernate)? Tieni tutti gli n-mila record in memoria? Cosa usi per generare il file excel (intendo quale libreria)?
    Fai cose "strane" con le stringhe ad esempio facendo moltissime concatenazioni in ciclo con l'operatore + ? Vengono fatti molti auto-boxing dei primitivi senza magari che te ne sia accorto? Ci sono dei memory-leak di cui non hai facilmente evidenza?
    Insomma, sarebbe da analizzare meglio il sorgente.
  • Re: Java.lang.OutOfMemoryError: GC overhead limit exceeded

    Quello che mi interesserebbe sapere se c'è un metodo per far si che funzioni tutto normalente anche su PC con poca memoria sia che devo estrarre pochissimi record ma che funzioni in egual modo quando devo estrarre un numero significativo di record.

    Posto il codice così magari saprete aiutarmi meglio:
    
    package prova;
    
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.ResultSetMetaData;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.time.LocalDateTime;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import org.apache.poi.ss.usermodel.Cell;
    import org.apache.poi.ss.usermodel.Row;
    import org.apache.poi.ss.usermodel.Sheet;
    import org.apache.poi.ss.usermodel.Workbook;
    import org.apache.poi.xssf.usermodel.XSSFWorkbook;
    
    public class Prova{
    
    	private static final String DRIVER = "oracle.jdbc.driver.OracleDriver";
    	private static final String HOST = "localhost";
    	private static final String PORT = "1521";
    	private static final String SID = "orcl";
    	private static final String URL = "jdbc:oracle:thin:@" + HOST + ":" + PORT + ":" + SID;
    	private static final String USER = "hr";
    	private static final String PASSWORD = "hr";
    	private static final String QUERY_ESTRAZIONE = "select * from hr.t_test";
    	private static List<String> listHeader;
    	private static List<Map<Integer, String>> listBody;
    
    	public static void main(String[] args) {
    	    System.out.println("Start project list: " + LocalDateTime.now());
    	    createXLSXFile("PPM_Project_List.xlsx", "PROVA", QUERY_ESTRAZIONE);
    		System.out.println("End project list: " + LocalDateTime.now());
    
    	}
    
    	public static void createXLSXFile(final String excelFileName, final String sheetName, final String query) {
    		listHeader = new ArrayList<>();
    		listBody = new ArrayList<>();
    
    		try {
    			Class.forName(DRIVER);
    		} catch (ClassNotFoundException e) {
    			e.printStackTrace();
    		}
    
    		try (final Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
    				final Statement stmt = conn.createStatement();
    				final ResultSet rset = stmt.executeQuery(query)) {
    			final ResultSetMetaData rsmd = rset.getMetaData();
    
    			for (int i = 1; i <= rsmd.getColumnCount(); i++) {
    				listHeader.add(rsmd.getColumnName(i));
    			}
    
    			while (rset.next()) {
    				final Map<Integer, String> map = new HashMap<>();
    				for (int k = 0; k < rsmd.getColumnCount(); k++) {
    					map.put(k, rset.getString(k + 1));
    				}
    				listBody.add(map);
    			}
    		} catch (SQLException e) {
    			e.printStackTrace();
    		}
    
    		try (Workbook wb = new XSSFWorkbook(); OutputStream fileOut = new FileOutputStream(excelFileName)) {
    			Sheet sheet = wb.createSheet(sheetName);
    
    			addHeader(sheet);
    			addBody(sheet);
    
    			wb.write(fileOut);
    			fileOut.flush();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    
    	}
    
    	private static void addHeader(final Sheet sheet) {
    		Row rowHeader = sheet.createRow(0);
    		Cell cell = null;
    		for (int i = 0; i < listHeader.size(); i++) {
    			cell = rowHeader.createCell(i);
    			cell.setCellValue(listHeader.get(i));
    		}
    	}
    
    	private static void addBody(final Sheet sheet) {
    		Row row = null;
    		Cell cell = null;
    		Map<Integer, String> entry = new HashMap<>();
    		for (int i = 0; i < listBody.size(); i++) {
    			row = sheet.createRow(i + 1);
    			entry = listBody.get(i);
    
    			for (Integer key : entry.keySet()) {
    				String value = entry.get(key);
    				cell = row.createCell(key);
    				cell.setCellValue(value);
    			}
    		}
    	}
    
    }
    
    Quello che mi chiedo è se ci sarebbe il modo di migliorarlo affinché non mi restituisca più quell'errore.
  • Re: Java.lang.OutOfMemoryError: GC overhead limit exceeded

    CRTVLB ha scritto:


    Quello che mi interesserebbe sapere se c'è un metodo per far si che funzioni tutto normalente anche su PC con poca memoria sia che devo estrarre pochissimi record ma che funzioni in egual modo quando devo estrarre un numero significativo di record.
    In teoria sì ma più hai interesse a gestire "molto" con "poche" risorse, allora più bisogna essere assolutamente oculati ed attenti nella gestione della memoria!

    Già il fatto di tenere una lista di mappe (quel List<Map<Integer, String>>) non aiuta affatto ed È un problema.
  • Re: Java.lang.OutOfMemoryError: GC overhead limit exceeded

    andbin ha scritto:



    In teoria sì ma più hai interesse a gestire "molto" con "poche" risorse, allora più bisogna essere assolutamente oculati ed attenti nella gestione della memoria!

    Già il fatto di tenere una lista di mappe (quel List<Map<Integer, String>>) non aiuta affatto ed È un problema.
    Intanto grazie per la risposta, mi sapresti dare delle dritte per capire come migliorarlo?

    Ad esempio invece di tenere una lista di mappe come potrei fare?

    C'è altro che potrei migliorare?
  • Re: Java.lang.OutOfMemoryError: GC overhead limit exceeded

    CRTVLB ha scritto:


    Ad esempio invece di tenere una lista di mappe come potrei fare?
    Qualcosina di meglio lo puoi fare tenendo una lista di array. La query dà un certo numero di colonne e per tutto lo scorrimento del ResultSet sono quelle tot colonne.

    Qualcosa di ancora meglio sarebbe evitare la lista: per ciascun record vai ad aggiungere subito "al volo" una riga nello Sheet (per fare questo devi ristrutturare un po' il tuo codice). Quindi la lettura non occupa più particolare memoria mentre l'unica occupazione è data dal modello ad oggetti nel Workbook di Apache POI.

    Se poi sai a priori che tra tutti i record ci sono moltissime stringhe duplicate, allora si potrebbe anche pensare ad una deduplicazione degli oggetti String. Ma è una cosa un po' particolare e da valutare.
  • Re: Java.lang.OutOfMemoryError: GC overhead limit exceeded

    andbin ha scritto:



    Qualcosa di ancora meglio sarebbe evitare la lista: per ciascun record vai ad aggiungere subito "al volo" una riga nello Sheet (per fare questo devi ristrutturare un po' il tuo codice). Quindi la lettura non occupa più particolare memoria mentre l'unica occupazione è data dal modello ad oggetti nel Workbook di Apache POI.
    Credo che questa sarebbe la soluzione migliore.

    Dove posso trovare del materiale al riguardo? Così mi documento e faccio delle prove.

    Grazie ancora.
  • Re: Java.lang.OutOfMemoryError: GC overhead limit exceeded

    CRTVLB ha scritto:


    Dove posso trovare del materiale al riguardo?
    Scusa .. ma di che materiale stai parlando?? Devi solo spostare/ristrutturare le cose nel tuo codice, non è scienza nucleare ....
    Ad esempio la creazione del XSSFWorkbook dovrà essere prima del ciclo sul ResultSet. E quel addBody non ha molto più senso che esista.
  • Re: Java.lang.OutOfMemoryError: GC overhead limit exceeded

    Ok grazie, faccio delle prove.

    La cosa che mi viene difficile è far inserire i record al volo invece di come ho fatto, per quello stavo cercando del materiale(es. siti con degli esempi).

    Cmq ci provo e vedo che ne viene fuori, in caso poi posto quello che ho fatto così se ti va ne possiamo discutere insieme e mi spieghi cosa va e cosa non va.

    Ti ringrazio per gli spunti che mi hai dato.
  • Re: Java.lang.OutOfMemoryError: GC overhead limit exceeded

    CRTVLB ha scritto:


    La cosa che mi viene difficile è far inserire i record al volo invece di come ho fatto, per quello stavo cercando del materiale(es. siti con degli esempi).
    Concettualmente, abbozzato:
    1) Esegui la query ed ottieni il ResultSet
    2) Ottieni i metadati (ResultSetMetaData)
    3) Crea il XSSFWorkbook
    4) Crea la riga di intestazione nello sheet
    5) while "se c'è prossimo record":
    ....5b) Estrai le colonne del record
    ....5c) Aggiungi riga (con quelle colonne) nello sheet
    6) chiusure, scritture ecc...

    Sono le stesse cose che già fai, solo spostate e messe diversamente.
  • Re: Java.lang.OutOfMemoryError: GC overhead limit exceeded

    Ti ringrazio, così mi è più chiaro.

    Appena ho un po' di tempo ci provo e ti faccio sapere.

    Gentilissimo.
  • Re: Java.lang.OutOfMemoryError: GC overhead limit exceeded

    Se vuoi fare il tutto in modo sbrigativo e senza particolari design (cioè con tutti i campi/metodi static come si è visto) fai pure.
    Se vuoi fare un pelino meglio, incapsula la sola logica di gestione del documento excel in una classe a parte.
  • Re: Java.lang.OutOfMemoryError: GC overhead limit exceeded

    andbin ha scritto:


    .
    Se vuoi fare un pelino meglio, incapsula la sola logica di gestione del documento excel in una classe a parte.
    Sicuramente mi piacerebbe farlo un pelino meglio, ma non credo di essere abbastanza bravo e poi in realtà era più di un anno che avevo tralasciato Java, sono un po arrugginito

    Ma visto che oggi ho qualche ora libera ci provo.
Devi accedere o registrarti per scrivere nel forum
13 risposte