Italian B4J - Error Java OutOfMemoryError

marco.canta

Active Member
Licensed User
Longtime User
Buongiorno, ho realizzato diversi programmi con il B4j e sempre con molta semplicità e soddisfazione, questa volta ho realizzato un software che deve girare senza essere mai chiuso 24h su 24, e che legge, scrive e aggiorna un database mySQL (MAMP).
Il programma sembra funzionare ed eseguire tutte le sue procedure, unico problema (e non da poco), ogni tanto si blocca e arriva questo errore :

Java.lang.OutOfMemoryError: Java heap Space

Non so cosa lo possa generare e quindi dove andare a risolvere, mi occorre un vostro aiuto.

Grazie Marco.



Errore_Java.png
 

udg

Expert
Licensed User
Longtime User
Una prima misura è aumentare lo spazio heap a disposizione della JVM.
Ma è probabile che ci sia qualcosa nel programma che richiede risorse che poi non libera e quindi il garbage collector (quando, con comodo, decide di partire) non libera tutto ciò che potrebbe e passo dopo passo il tuo heap si satura.

Qui trovi un paio di spunti.
 

Star-Dust

Expert
Licensed User
Longtime User
Fallo girare in debug e vedi se identifichi la riga che genera l'errore
 

marco.canta

Active Member
Licensed User
Longtime User
Ho fatto girare in Debug, ma non sono usciti errori. Credo che sia un problema legato alla memoria e alla gestione del mySQL.
Io faccio molti accessi al mySQL e forse è proprio quello che blocca .


ad inizio programma creo la connessione .... che poi chiudo solamente a chiusura programma.

B4X:
Sub DB64_Connect()
    Try
        Main.mySQL.Initialize2("com.mysql.jdbc.Driver", "jdbc:mysql://" & Main.ValIP_mySQL & ":" & Main.ValPort_mySQL & "/mySQL?characterEncoding=utf8",               Main.ValUser_mySQL, Main.ValPass_mySQL)
        Log("- mySQL Connected")
    Catch
        Log("*! mySQL  ** Error **")
    End Try
End Sub



e ogni 250 millisecondi (tramite TIMER) faccio la lettura o scrittura alle tabelle con un codice come il seguente :

B4X:
Sub mySQL_Read()
    Dim rs As ResultSet = Main.mySQL.ExecQuery("SELECT * FROM Impianto")
    rs.NextRow
        Main.ValCodImpianto = rs.GetString("CodImpianto")
        Main.ValDescImpianto = rs.GetString("DescImpianto")
    rs.Close
End Sub

qualche aiuto o suggerimento ?

Grazie Marco.
 

udg

Expert
Licensed User
Longtime User
Erel suggersice l'utilizzo del ConnectionPool. Qualcosa tipo:

B4X:
Sub Process_Globals
   Private pool As ConnectionPool
End Sub

Public Sub InitDB
   pool.Initialize("com.mysql.jdbc.Driver", "jdbc:mysql://localhost:3306/?characterEncoding=utf8&useSSL=false&zeroDateTimeBehavior=convertToNull",...)
End Sub

Public Sub getData
  ...
   Dim sql1 As SQL = pool.GetConnection
   sql1.BeginTransaction
   Try
       Dim Cursor As ResultSet
       Cursor = sql1.ExecQuery($"SELECT * FROM ...;"$)
       Do While Cursor.NextRow
           .....
       Loop
        ..
       sql1.TransactionSuccessful
   Catch
       ...
       sql1.Rollback
   End Try
   Cursor.Close
   sql1.Close
End Sub

In pratica quando devi accedere ai dati ti fai dare un nuovo oggetto dal pool e quando hai finito lo chiudi (così come per il RS). Ci pensa il pool a rilasciare la connessione.
 
Last edited:

udg

Expert
Licensed User
Longtime User
Di nulla. Valuta anche se nel tuo caso le transazioni siano necessarie.
Mi sembra che tu legga un unico record; in tal caso completa lo statemenet con "LIMIT 1" in modo che il cursor non debba recuperarli tutti per poi utilizzarne uno solo. Presumendo che tu desideri quello più recente sarebbe bene introdurre un "WHERE" su un campo data o data/ora o al peggio sull'id dove selezioni il maggiore.
 
Last edited:

marco.canta

Active Member
Licensed User
Longtime User
Grazie del suggerimento, si in effetti uso spesso la lettura di un solo record e lo filtro con un WHERE ...


Fatte prime prove e primi errori ...

per la connessione utilizzo questo connector ... "#AdditionalJar: mysql-connector-java-5.1.27-bin.jar"
e connetto il DB mySQL che gira su MAMP con il seguente codice :

B4X:
    Try
        Log("try ... InitDB")
        ConPool.Initialize("com.mysql.jdbc.Driver", "jdbc:mysql://localhost:8889/DB64?characterEncoding=utf8&useSSL=false&zeroDateTimeBehavior=convertToNull", "root", "root")
    Catch
        Log("Last Pool Init Except: "&LastException.Message)
    End Try

e questo è l errore a cui non riesco a venirne a capo ...
error.png


e ora dove metto le mani ? ehehehe ....
 

LucaMs

Expert
Licensed User
Longtime User
Donq... siccome non me va de pensa' troppo ma di dare una zampa sì, pubblico direttamente il codice.

E' in un modulo, una routine pubblica Init:
B4X:
Dim JdbcUrl As String = Main.settings.Get("JdbcUrl")
Dim driverClass As String = Main.settings.Get("DriverClass")
Dim dbuser As String = Main.settings.Get("DBUser")
Dim dbpassword As String = Main.settings.Get("DBPassword")

pool.Initialize(driverClass, JdbcUrl, dbuser, dbpassword)

Vado a vedere il resto (cosa diavolo ci sia in quella che suppongo sia una map, settings ed aggiungo a questo stesso post)...


Infatti è una map (nel Main, come si vede nel codice sopra):
settings = File.ReadMap(File.DirApp, "settings.txt")


#AdditionalJar: C:\Program Files (x86)\MySQL\MySQL Connector J\mysql-connector-java-5.1.32-bin.jar


file settings.txt:

JdbcUrl=jdbc:mysql://localhost/MyGame1?characterEncoding=utf8
DriverClass=com.mysql.jdbc.Driver
DBUser=[omissis]
DBPassword=[omissis]
DBMyGameAdmin=[omissis]
DBMyGameAdminPassword=[omissis]
 

udg

Expert
Licensed User
Longtime User
Quello del post #9 non è un errore. Sono le prime righe dell'attivazione.
Poi dovresti avere qualcosa tipo :
"2018-05-12 10:20:57.170:INFO::main: Logging initialized @855ms to org.eclipse.jetty.util.log.StdErrLog"
ed altre, fino a:
"2018-05-12 10:20:57.493:INFO:eek:ejs.Server:main: Started @1178ms"

A meno che per "errore" non intendi che tutto si pianta dopo quelle 4 righe..
 

LucaMs

Expert
Licensed User
Longtime User
Oh, poi, per quanto riguarda le query... una routine che uso (non adatta per tutto, non in scrittura, ad esempio):
B4X:
Private Sub ExecNonQuery2(Query As String, Args As List) As Boolean
    Dim Result As Boolean = True
   
    Dim sq As SQL = pool.GetConnection
    Try
        sq.ExecNonQuery2(mQuery, Args)
    Catch
        Log(LastException)
        Result = False
    End Try
    sq.Close  
   
    Return Result
End Sub
 

marco.canta

Active Member
Licensed User
Longtime User
LucasMs ... rileggendo il tuo post precedente dove dicevi che non vedevi errori ... continuato a scrivere codice e leggere i record ....

Funziona !!! leggo correttamente i record ... ora cerco di ottimizzare il codice.

tra le cose che avevo letto sul forum avevo trovato anche questa parte di codice da inserire dopo la connessione

B4X:
  Dim jo As JavaObject = ConPool
    jo.RunMethod("setMaxPoolSize", Array(100))

potrebbe avere senso ?
 

udg

Expert
Licensed User
Longtime User
Considerando che prevedi di aprirne 4 al secondo, presumo che un limite ridotto possa facilmente condurre ad errori di mancata allocazione. Un limite alto invece dovrebbe condurre a maggior occupazione di memoria. Puoi cercare un equilibrio tra i due fattori ed utilizzare quel codice per "restare al comando"; in caso di problemi sapresti dover metter mani.
 

marco.canta

Active Member
Licensed User
Longtime User
Ho fatto un programma per Test ... ora lo lascio girare per qualche ora e vi aggiorno.

per ora Grazie del supporto :)
 

marco.canta

Active Member
Licensed User
Longtime User
Al momento ancora sembra funziona senza bloccarsi .

Ma se volessi utilizzare due database mySQL1 e mySQL2 posso utilizzare una sola ConnectionPool o ne devo istanziare due distinte ?
 
Top