čtvrtek 26. ledna 2006

JDBC memory leak za 5,50-

Je libo si dát malý, únik paměti, který ve výsledku povede k pádu aplikace na java.lang.OutOfMemory? Budeme potřebovat tyto ingredience:

  • kód pracující s databází přes JDBC
  • poolování databázových připojení

To nejdůležitější ovšem představuje následující konstrukce

PreparedStatement pstmt = null;
try {
 
Connection con = //ziskej DB connection z poolu
 
pstmt = con.prepareStatement("select ...");
  
 
//udelas dootaz, zpracujes bysledky
  //a pak vytvoris novy statement, bez toho
  //aniz by si uzavrel ten predchozi

 
pstmt = con.prepareStatement("select ...");

} finally {
 
//ve finally sice zaviras statement,
  //ale to se jedna samozrejme az o tu
  //druhou instanci
 
DatabaseUtils.closeStatement(con, pstmt);
}

Vysvětlím proč k tomu memory leaku dochází, protože to není možná na první pohled patrné. I když je proměnná pstmt lokální a zdá se, že by garbage collector mohl z ní referencovaný objekt uklidit, není tomu tak. Statement je totiž ještě referencován z databázového připojení (con).

Protože jsou databázová připojení poolovaná, tak nezavřené statementy nich dohnívají do té doby, dokud nezaberou tolik paměti, že dojde k java.lang.OutOfMemory. Alespoň takovou zkušenost jsem udělal s Oracle JDBC driverem.

No a na závěr připomenu, že nejlepší ochranou je prevence třeba pomocí toho, že starost o statement objekt necháte někomu jinému viz Návrhový vzor Template method a jeho aplikace v prostředí JDBC

úterý 24. ledna 2006

Vrchol neoptimálnosti

Jsou "programátorské perly", které by stálo za to někde vystavit. Jednou z takovýchto "perel" je i následující magický kousek kódu, při jehož čtení vám spadne brada dolu. Soukromě jsem ho nazval "vrchol neoptimálnosti" a to nejsem nějaká citlivka.

private boolean canConvertValue(String fieldName) {
   
for (int i = 11000; i < 16000; i++)
     
if (fieldName.compareTo("Z" + i) == 0) return false;
   
// pokud nic nenajdu, tak muzu konvertovat
   
return true;
}

Tato metoda je navíc v kontextu volána velice neoptimálně. fieldName je název sloupečku v databázi a metoda se volá pro každý vrácený řádek z resultsetu. Co zpracovaný řádek to přibližně 100, slovy jedno sto, volání této metody. Za 50 minut profilování CPU, se v této metodě strávilo 4,75% strojového času. Celkově se pouze v této metodě strávilo 270 sekund. Naštěstí se vlastní SQL volalo za těch 50 minut pouze třiatřicetkrát.