25 Kasım 2010 Perşembe

java.lang.OutOfMemoryError

Merhaba arkadaşlar,

     Programlar bilgisayarlarımıza ilk kurulduklarında hangi argümanlara göre çalışacaklarına kendileri karar verirler. Default olarak gelen bu değerler her zaman uygulamamızın doğasına uygun olmayabilir. Örneğin enterprise uygulamalar üzerinde çalışıyorsak bu bu başlangıç değerleri projemizin isteğini karşılamıyor olabilir. Daha fazla bellek alanı tahsis etmemiz, işlemciyi isteklerimize uygun olarak kullanmak isteyebiliriz. Hatta belirlediğimiz uygulamaların bu kaynakları ne şekillerde paylaşacaklarını ayarlamamız da gerekebilir.

     Böyle durumlarda programların default olan değerlerini ihtiyacımıza göre değiştirmemiz gerekecektir. Bu gerek veritabanı, gerek uygulama çatısı olsun platformlar üzerindeki yönetimizi kolaylaştıran unsurlardır.

    Uygulamanızı uygulama sunucusunda deploy ederken, veya uygulamamız runtime'da iken bellek yetersizliği ile ilgili hatalar almış olabilirsiniz. Böyle durumlarda sunucuda kullanıma açılan bellek alanının yetersizliğinden dolayı uygulamamız kendisine gerekli olan alanı yaratamaz. Heap alanındaki aşırı doluluk durumu Garbage Collection ın temizliği sonrasında hafifletilebilmektedir. Ancak PermGen alanının statik olması uygulamaların ihtiyacı olan bellek alanının tahsis edilemediği durumlarda " java.lang.OutOfMemoryError " hatasına sebep olmaktadır. Java Sanal Makinası-JVM e dair bu bilgileri anlık kullanım verilerini elde etmek için JDK nızın içerisinde bulunan Java Virtual VM uygulamasını kullanabilirsiniz. C:/Program Files/Java/[ilgili jdk klasörü]/bin/jvisualvm altıdna uygulamaya erişebilirsiniz. Bilgisayarımdaki heap ve permgen kullanımınlarına dair görüntü aşağıdaki gibi.


Heap Alanı


PermGen Alanı

    Görüldüğü gibi hep alanı Garbage Collection çalıştığında kullanımı düşmekte ama PermGen alanı ise sürekli aynı kalmaktadır. Bu sebeple uygulamamıza PermGen alanını daha fazla vermek isteyebiliriz. Böyle zamanlarda uygulamamızın çalıştırılma konfigürasyonunu değiştirmemiz gerekir. Eclipse için konuşursak, projemizi (Run veya Debug) ayağa kaldırdığımız seçimimizin configuration ayarlarına girmemiz gerekiyor. Bu ayarlar da Eclipse araç çubuğunun Run sekmesi altında bulunmakta.

    Run >> Debug Configurations  a girdiğimizi varsayalım. Uygulamamızı varolan menüde seçtikten(veya yarattıktan) sonra uygulamanın Arguments sekmesi altındki VM arguments kısmına

-Xms512m -Xmx1024m -XX:CompileThreshold=8000 -XX:PermSize=96m  -XX:MaxPermSize=256m
parametreleri yapıştırılmalıdır. Buradaki "-Xms512m -Xmx1024m" argümanları uygulama sunucusu için ayrılan Heap miktarlarını ifade ederken "-XX:PermSize=96m  -XX:MaxPermSize=256m" argümanları da PermGen alanının düzenlenmesi için kullanılmaktadır. Kendi RAM miktarınıza göre ayarlamanızı yapabilirsiniz. Ben  2 GB lık RAM in bulunduğu bilgisayarımda heap için 512m ve 1024m kullanırken PermGen için 256m ve 512m kullanıyorum.

İyi çalışmalar herkese, tekrar görüşmek üzere.

8 Kasım 2010 Pazartesi

Transaction marked as rollbackOnly

   Merhabalar,

   Dün karşılaştığım bir bilgiyi paylaşmak istedim bugün. Spring MVC 3.0 üzerisinde çalışıyordum. Hibernate kullanıyorum ve veritabanına bir insert yapmam gerekiyordu. Tabii ki Entity Manager'ın bir örneğini aldıktan sonra ona veritabanındaki tablonun pojosu türünden bir nesne göndermem gerekiyordu. Buraya kadar herşey gayet güzel, gayet hoş. İşler tıkırında herşey çalışıyor. Kodum da aşağıda.

@Transactional
public void insertName(TmFrstDnm name) throws DataAccessException {
   try {
         entityManager.persist(name);
         entityManager.flush();
        } 

   catch (Exception e) {
         e.printStackTrace();
        }
}


   Eski zamanlardan kalma alışkanlık, ucu veritabanına dokunan her işlemi try catch bloguna aldım. Ve editor debug aşamasında bas bas bağırdı.

"org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly"

   "Transaction marked as rollbackOnly"  mi :S ?? Nasıl olur, ben commit edemeyeceğim datayla niçin oynayayım. Tablom CRUD işlemlerini yoğun olarak barındıran bir tablo. Eğer rollback yapacaktıysam Temprorary Table kullanırdım, hem de Oracle'ın üzerisindeki yükü hafifletirdim.(Bknz Redo Log Oluşumu)

    Biraz araştırınca sebebi ortaya çıktı. Entity Manager'ın  yapacağı işlemleri try catch blogu içerisine aldığımızda kodun çalışması garanti olamıyor. Yani şöyle örnekleyelim, yukarıdaki flush işleminin altında da kod parçaları olduğunu düşünün. O kod parçasında bir exception olduğu zaman try işleminin içerisinde yapılan işlemin tamamının iptal edilmesi, doğrudan catch blogunun içerisinin işletilmesi gerekir. Ancak bu exception'dan önce flush işlemi yapıldığından veritabanı commit'lenmiş olacak, işlemin geriye alınması mümkün olmayacaktır. İşte bunu önleyebilmek için try-catch blogu içerisindeki veritabanı işlemleri otomatik olarak rollback only yapılıyor. Bu sebeple try içerisindeki flush'ım çalışmıyor.

   Peki neler yapılabilir? Flush try'ın dışarısına çıkarılabilir. Ortaya çıkan exception'lar fırlatılarak DAO katmanında yakalanmaktansa service veya controller katmanında yakalanabilir. Veya try-catch bloguna bir de finally eklenerek burada commit yapılmak istenebilir ama ne kadar tercih edilesi bir durumdur tartışılır.

  İşte böyle, ilginç ama güzel bir senaryo. Tekrar görüşmek üzere