Kurumsal Java Yazılımı

61 views
Skip to first unread message

Kurumsal Java Yazılımı - Özcan Acar

unread,
May 12, 2012, 10:37:21 AM5/12/12
to kurumsaljava+...@googlegroups.com

Kurumsal Java Yazılımı

Link to Kurumsal Java Yazılımı - Özcan Acar

Matrix’de Yaşayan Programcılar

Posted: 12 May 2012 04:52 AM PDT

Hemen hemen her programcının Matrix filmini seyrettiğini düşünüyorum. Star Wars gibi Matrix filmi de biz yazılımcılar için bir kült. Biraz abartı da olsa fikir olarak çok enteresan, en azından bir yazılımcı için. Matrix’de kullanılan yazılım sistemi dikkat çekiyor. En çok ilgimi çeken dejavü olarak isimlendirilen yazılım hataları (bug) ve Neo’nun bir tren istasyonunda hapis kalması ve trene binmesine rağmen tekrar tekrar aynı istasyona geri dönmesi, yani bir nevi for döngüsü olmuştur. Bir for döngüsünün bu kadar güzel görselleştirilmesi beni çok etkilemişti. Böyle bir sistemin entegrasyon testleri nasıl yapılıyor acaba?

Gelelim gerçek hayattaki Matrix’e. Java ile program yazan programcılar da yazdıkları programlar gibi bir Matrix içinde yaşarlar. Bu dünyanın ismi JVM – Java Virtual Machine yani Java Sanal İşlemcisi‘dir. JVM C++ dilinde yazılmış bir programdır. Bir Java programı javac.exe ile derlendikten sonra byte code ismi verilen bir ara sürüm oluşur. Byte code, ana işlem biriminin (CPU – Cental Processing Unit) anlayacağı cinsten komutlar ihtiva etmez, yani klasik Assembler değildir. Java byte code sadece JVM bünyesinde çalışır. JVM, derlenen Java programı için ana işlemci birimi olma görevini üstlenir. Bu özelliginden dolayı Java programlarını değişik platformlar üzerinde çalıştırmak mümkündür. Her platform için bir JVM sürümü Java programlarını koşturmak için yeterli olacaktır. Bu sebepten dolayı Java “write once, run anywhere – bir kere yaz, her yerde koştur” ünvanına sahiptir.

Java programları java.exe komutu kullanılarak koşturulur. java.exe işletim sistemi bünyesinde bir JVM meydana getirir yani Matrix’i oluşturur. Bu Matrix Java programının yaşaması için gerekli ortamı ihtiva eder. Sıra dışı olmayan Java programları bu Matrix içinde dış dünya ile ilişkileri kesik bir şekilde yaşayıp giderler. Onların ihtiyaç duyduğu herşeyi Matrix onlara sunar. Java programları hangi işletim sistemi ya da hangi donanım üzerinde koştuklarını bile zorda kalmadıkça bilmezler. Onlar için her donanım üzerinde bir integer 4 byte yani 32 bittir. Bu sebepten dolayı Java’da C/C++ dan tanıdığımız unsigned int bulunmaz. İnteger hangi donanım olursa olsun 32 bittir, işletim sistemi ya da donanıma göre değişmez. Bu Java dilini tasarlayan mühendislerin Java programcılarının hayatını kolaylaştirmak için aldıkları bir tasarım kararıdır. Bir Java programının gördüğü alt yapı her zaman aynıdır. Java programcıları bunu bildikleri için Matrix haricinde olup bitenlere pek önem vermeden kodlarını yazarlar ve Matrix içinde yaşamaya devam ederler, taki Morpheus gelip programcıya mavi ve kırmızı hapları taktim edene kadar.

Klasik kurumsal Java projelerinde çalışan Java programcısı Matrix’in dışında olup bitenlerle ilgilenmez. JVM ona ihtiyaç duyduğu herşeyi sağlar. O yüzden programcının tercihi mavi hap ve Matrix içinde yaşamaya devam etmek olur.

Java sadece kurumsal projeler için kullanılmaz. Sıra dışı projelerde de Java’ya rastlamak mümkündür. Şu an çalıştığım proje bunun en iyi örneklerinden birisidir. Navteq/Nokia firmasının GeoCoder (http://maps.nokia.com) isminde, harita üzerinde lokasyon arama yapan bir servisi bulunmaktadır. Bu servis aynı zamanda Bing ve Yahoo tarafından kullanılmaktadır. Kısa bir zaman sonra Facebook tarafından kullanılma planları yapılmaktadır. Ben lokasyon arama işlemlerinin programlandığı ekipte çalışıyorum. Bu gibi projelerde Morpheus’un verdiği kırmızı hapı yutup, Matrix’in dışına çıkmak gerekiyor, aksi taktirde Matrix, yani JVM içinde kalarak uygulamanın tabiyatındaki sıra dışılığı anlamak ve kodlamak mümkün değildir. Bu sıra dışılık programcıyı çok daha değişik kategorilerde düşünmeye ve değişik disiplinlerde çalışmaya zorlamaktadır.

Nokia’nin lokasyon arama servisi için dünya çapında altı değişik hosting lokasyonunda 300 den fazla VM (virtual Machine – sanal sunucu) kullanılıyor. Aşağıdaki resimde de görüldüğü gibi her JVM işletim sistemi bünyesinde 40 GB’den daha fazla yer kaplıyor. Neden JVM için bu kadar büyük bir hafıza alanının kullanıldığını anlamak için, uygulamanın tabiyatını anlamak gerekiyor.

Yukarıda yer alan resimde işletim sistemi (RedHat Linux) JVM için yeni bir işlem (process) oluşturmuştur. Bu işlemin işletim sistemi bünyesindeki hafıza büyüklüğü toplamda (VIRT – Virtual – sanal) 44GB’dir. JVM için kullanılan hafıza (Heap size) 5 GB’dir. Heap ayarı -Xmx5g ile yapılmaktadır. Peki geriye kalan 39 GB neyin nesidir? Bunu anlamak için Matrix’in dışına çıkmamız gerekiyor, çünkü geriye kalan 39 GB Matrix’in dışında olup bitenleri temsil etmektedir.

Lokasyon arama servisi için klasik veri tabanı sistemi kullanılmamaktadır. Klasik bir veri tabanı sisteminin kullanılması ve lokasyon arama işlemlerinin bu veri tabanı üzerinden yapılması arama süresini dakikalara çıkarabilir. Arama sonuçlarının 100 ms (100 milisecond bir saniyenin onda biridir) gibi bir zaman diliminde oluşturulması şartı bu projede klasik veri tabanlarının aksine bir tercihi zorunlu kılmıştır. Veri tabanından çekilen verilerle dosya tabanlı yeni bir veri tabanı oluşturulmakta ve bu dosyalar onlarca GB büyüklükte olabilmektedir. Bu veri tabanında bulunan verilere index dosyaları üzerinden erişilmektedir. Örneğin kullanıcı İstanbul Kadıköy lokasyonunu aradığında, arama işlemi önce index dosyasında yapılmaktadır. İndex dosyasında İstabul Kadıköy için bir veri bulunduğunda, bu veri adres nesnesinin dosya veri tabanındaki gerçek adresini (storage id) ihtiva etmektedir. Bu şekilde index üzerinden dosya veri tabanındaki adres nesnesine ulaşmak mümkün olmaktadır. Arama işlemlerinin hızlı yapılabilmesi için bu index ve diğer veri tabanı dosyalarının topluca hazıfaya yüklenmesi gerekmektedir. Aksi taktirde arama işlemleri çok uzun sürebilmektedir, çünkü arama esnasında hafızada yüklü olmayan bir adres nesnesi bulundu ise, bu nesnenin disk üzerinde bulunan veri tabanı dosyalarından yüklenmesi gerekmektedir. Bu gibi IO (Input/Output) işlemleri zaman aldığı için, genel olarak arama işlemi bu gibi durumlarda uzamaktadır. Bunun önüne geçmek için tüm dosyaların hafızaya yüklenmesi gerekmektedir. Bu işlemi yapmak için de Java Memory Mapped Files yapıları kullanılmaktadır.

-bash-3.2$ pmap PID komutunu girdiğimizde 39 GB alanın ne için kullanıldığını görebiliyoruz.

Yukarıda yer alan resimde görüldüğü gibi uygulama bünyesinde kullanılan tüm dosyalar işletim sistemi tarafından oluşturulan işleme (process) dahil edişmiştir. Bu dosyalara Java terminolojisinde Memory Mapped Files (hazıfada yüklü dosyalar) ismi verilmektedir. Yüksek performansın önemli olduğu durumlarda bu dosyaların %100 hafızaya yüklenmiş olmaları büyük önem taşımaktadır. Aksi taktirde dosyaların kullanılmak istendiğinde hafızada olmamaları performansı kötü etkilemektedir. Kullanılan hafıza alanının arkasında bir dosya yoksa yani bir memory mapped file kullanılmıyorsa, bu alanlar [ anon ] (anonim) olarak listede gözükmektedir.

Bizim örneğimizde ihtiyaç duyulan tüm dosyaların hepsinin %100 hafızaya yüklenmediğini görüyoruz. İlk resimde yer alan RES (resident – aktüel işgal edilen hazıfa alanı anlamnda) kolonuna göre JVM’in aslında 36 GB hafıza alanı işgal etmektedir. İşletim sistemi biz zorlamadıkça kullanılan tüm dosyaları %100 hafızaya yüklemez. Bu şekilde örneğin kullanılmayan fonsiyon kütüphanelerinin hafızada boş yere yer işgal etmesi engellenmiş olur. Bunun yanısıra işletim sistemi her program tarafından kullanılan ortak fonksiyon kütüphanelerini sadece bir kez hazıya yükleyerek, bu dosyaların değişik işlemler (process) tarafından ortaklaşa kullanılmasını sağlar. Birinci resimde SHR (shared – ortaklaşa kullanılan hafıza alanı anlamında) kolonuna baktığımızda bu değerin 12 GB oldugunu görmekteyiz, yani başka programlarla paylaştığımız 12 GB büyüklügünde dosyalar işlemci hafızamıza (Java Process Heap) yüklenmış durumdadır.

Belirttigim gibi bu dosyalar 5 GB’lik Java Heap içinde yer almamaktadırlar. Bu dosyalar daha önce bahsettiğim 39 GB’lik hafaza alanında yer almaktadir. Bu hafıza alanına Java Process Heap, normal Java nesnelerinin yer aldığı alana ise Java Heap ismi verilmektedir. Java Heap ve Java Process Heap bir araya geldiğinde 44 GB’lik, ilk resimde gördüğümüz Java işleminin tüm hafıza alanı ortaya çıkmaktadır. Java Process Heap alanı işletim sisteminden malloc() sistem fonksiyonu kullanılarak oluşturulan hafıza alanıdır. Java’da bu hafiza alanına genel olarak native memory ismi verilir. JNI (Java Native Interface) kullanılarak Java uygulamaları için native memory alanı oluşturmak ve kullanmak mümkündür. Java içinde ise hafıza alanı new operatörünü kullanarak tedarik edilir. Bu hafıza alanı oluşturduğumuz nesne için direk Java Heap bünyesinden gelir ve tamamen JVM tarafından, daha doğrusu Garbage Collector (kullanılmayan nesnelere garbage ismi verilir) tarafından yönetilir.

Yukarda yer alan resime baktığımızda JVM tarafından yönetilen hafıza alanının toplamda 5 GB olduğunu, bunun 1 GB’lik gibi bir kısmının EDEN Heap space, 150 MB’sinin iki SURVIVOR Heap Space ve 4 GB’sinin OLD Heap space tarafından kullanıldığını görmekteyiz. Toplamda JVM’in yönettiği ve içinde Java nesneleri oluşturabileceğimiz alan 5 GB büyüklüktedir.

Bahsettiğim index ya da veri tabanı dosyalarının normal heap içinde bulunmaları JVM’e fazladan yük getirmekte ve Garbage Collection işlemini uzatmaktadır. Bu tür dosyaları normal Heap içinden Java Process Heap’e (native memory alanına) taşımak için ByteBuffer sınıfı kullanılmaktadır. ByteBuffer.allocateDirect() metodu ile en fazla 2 GB büyüklüğünge, Java Heap dışında hafıza alanı rezerve etmek mümkündür. Bu hafıza alanı Java Process Heap içinde yer alacaktır. allocateDirect() metoduna baktğımızda native hafıza rezervasyonu için sun.misc.Unsafe sınıfının allocateMemory() metodunun kullanıldığını görmekteyiz. Bu native olarak tanımlanmış ve C++ dilinde kodlanmış bir metotdur. Buradan da anlaşıldığı gibi Java bünyesinde bu sınıfı kullanmadan native memory rezervasyonu mümkün değildir.

JVM bünyesinde tüm hafıza otomatik olarak JVM ve Garbage Collector tarafindan yönetilir. Programcının bu konuda yapması gereken fazla birşey yoktur. Java Heap dışında işlem yapmak zorunda kalındığında durum farklıdır. Native hafıza alanını JVM bünyesindeki Garbage Collector yönetmez. Programcının C/C++ dillerinde olduğu gibi native hafıza alanını kendisi yönetmesi gerekir. Bu yüzden Morpheus’un verdiği kırmızı hapı yutup, Matrix’in dışına çıkması gerekir. Sadece Matrix’in dışında olan programcılar gerçekte olup bitenlerden haberdardırlar.

EOF (End Of Fun)
Özcan Acar

Share/Bookmark
You are subscribed to email updates from Kurumsal Java Yazılımı - Özcan Acar
To stop receiving these emails, you may unsubscribe now.
Email delivery powered by Google
Google Inc., 20 West Kinzie, Chicago IL USA 60610

Kurumsal Java Yazılımı - Özcan Acar

unread,
May 13, 2012, 10:04:53 AM5/13/12
to kurumsaljava+...@googlegroups.com

Java Heap Nedir?

Posted: 12 May 2012 10:27 AM PDT

Herhangi bir Java sınıfından new operatörü ile bir nesne oluşturulduğunda, bu nesnenin bilgisayarın hafızasında konuşlandırıldığı alana Java Heap adı verilir.

Java Heap JVM tarafından oluşturulur ve aşağıdaki hafıza alanlarından oluşur:

  • Eden – new operatörü ile oluşturulan tüm nesneler önce bu hafıza alanında oluşturulur.
  • Survivor – nesneler old heap hafıza alanına transfer edilmeden önce bir müddet survivor hafıza alanlarında kalır. Buradaki amaç kısa ömürlü olan nesnelerin Garbage Collector tarafından toplanmasına ve old heap alanına geçmelerini engellemektir.
  • Old – uzun ömürlü nesnelerin ölene kadar kaldıkları hazıfa alanıdır.
  • Permanent – sınıfların ve statik değerlerin yer aldığı hazıfa alanıdir.

Yukarda yer alan resime baktığımızda JVM tarafından yönetilen hafıza alanının (Java Heap) toplamda 5 GB olduğunu, bunun 1 GB’lik gibi bir kısmının EDEN Heap space, 150 MB’sinin iki SURVIVOR Heap Space ve 4 GB’sinin OLD Heap space tarafından kullanıldığını görmekteyiz. Toplamda JVM’in yönettiği ve içinde Java nesneleri oluşturabileceğimiz alan 5 GB büyüklüktedir.

Share/Bookmark

JVM (Java Virtual Machine) Nedir?

Posted: 12 May 2012 10:18 AM PDT

JVM (Java Virtual Machine – Java Sanal İşlemcisi) C++ dilinde yazılmış bir programdır. Bir Java programı javac.exe ile derlendikten sonra byte code ismi verilen bir ara sürüm oluşur. Byte code, ana işlem biriminin (CPU – Cental Processing Unit) anlayacağı cinsten komutlar ihtiva etmez, yani klasik Assembler değildir. Java byte code sadece JVM bünyesinde çalışır. JVM, derlenen Java programı için ana işlemci birimi olma görevini üstlenir. Bu özelliginden dolayı Java programlarını değişik platformlar üzerinde çalıştırmak mümkündür. Her platform için bir JVM sürümü Java programlarını koşturmak için yeterli olacaktır. Bu sebepten dolayı Java “write once, run anywhere – bir kere yaz, her yerde koştur” ünvanına sahiptir.

JVM hakkında daha geniş kapsamlı bilgiyi Matrix’de Yaşayan Programcılar başlıklı yazımda bulabilirsiniz.

EOF (End Of Fun)
Özcan Acar

Share/Bookmark

Kurumsal Java Yazılımı - Özcan Acar

unread,
May 15, 2012, 10:36:14 AM5/15/12
to kurumsaljava+...@googlegroups.com

Mantığın Köleleri

Posted: 14 May 2012 09:11 AM PDT

Programcılık Sanat mı, Zanaat mı? başlıklı yazıma gelen yorumlar, programcılığın sanat olduğu yönünde. Bunun aksini düşünenler de var. Programcılığın bir sanat olarak algılanması subjektif ve bir yanılgıdır. Programcılığın bir sanat olmadığının altını bu yazımla tekrar çizmek istedim.

Sanat ve sanat eserleri görecelidir. İnsanlik var olduğundan beri sanat vardır, ama hala bu konudaki tartışmalar son bulmamıştır. „Renkler ve zenkler tartışılmaz“ sözünü sıkça işitmişizdir. Bu her bireyin sanat algılayış tarzının değişikliğine işaret etmektedir. Benim beğendiğim bir resmi, başka bir insan beğenmeyebilir. Bu yüzden sanatı tanımlamak ve bu işin içinden çıkmak çok karmaşık bir şeydir. Programcılığın da bir sanat olarak algılanıp, savunulması bu açıdan baktığımızda anlaşılabilir bir durumdur.

Bir sanat ya da sanat eseri ruha, göze ve gönüle hitap eder. Sanatın doğasında soyutluk vardır ve insanın kendisine özgün yorumuna ihtiyaç duyar. Bu yüzden herkesin sanatı algılayış tarzı değişiktir. Herkes sanatı kendi tarzında yorumlar. Eğer yazılan bir program parçası bir sanat eseri olmuş olsaydı, o zaman bu programa bakan herkes başka bir şey algılayacaktı ya da ondan kendi yorumladığı bir davranış biçimi bekleyecekti. Böyle bir şeyin yazılımda ne anlama geldiğini düşünebiliyor musunuz? Programcılık deterministik sonuçlar üretmek, mantık çerçevesinde kalıp, çözüm sunmak zorundadır. Ruha, gönüle değil, mantığa hitap eder. Sadece bu özelliğinden dolayı bile bir sanat olma şansı yoktur. Kim bir programa bakarsa baksın, aynı davranış biçimini görür, görmek zorundadır. Bir program her zaman aynı sonucu üretmek zorundadır, kod nasıl yazılmış olursa olsun. İnsanların yorumuna ihtiyacı yoktur, çünkü herkes aynı şeyi görür, daha dogrusu görmek zorundadır.

Programlar ihtiyaçtan doğarlar. Aynı şeyi sanat eserleri için söylemek mümkün değildir. Sanatkar bir şeylerden esinlenerek sanatını icra eder, zorda kalmadan, birilerine sanatını nasıl icra etmesi gerektiğini danışmadan. Programlar müşterileri tarafından kendi gereksinimlerini tatmin etmek için sipariş edilir. Müşterinin gereksinimleri programı yoğururken ön plandadır. Programı oluşturulurken müşteri ile iletişim esastır. Onun istediği olur.

Bunu yanısıra programcının kod yazarken soyutluğu ifade etmek için çok fazla seçenegi yoktur. Java dilinde interface ya da abstract sınıfları kullanarak soyut bir şeyler ifade edebilebilir. Bu ressamın paletinde sadece bir, iki renk olduğu anlamına gelir. Sadece iki rengi kullanarak sanat eseri oluşturmak mümkün müdür? Belki! Bir ressamın paletinde onlarca renk vardır, bunları karıştırarak sonsuz sayıda yeni renk elde edebilir. Bu renklerin hepsini kullanma potansiyeline sahiptir ve bu sanatına yansır. Günümüzün programlama dillerinde böyle bir zenginlik söz konusu değildir. Kaldı ki en tabana indiğimizde bir programcı sıfır ve birlerden oluşan verilerden başka bir şeyle ugraşmaz. Sadece iki renkli olan bir dünyada yaşar ve bir takım verileri A’dan B’ye taşıyacak programlar yazar. İşin hepi, topu budur.

Günümüzde nesneye yönetlik programlama paradigması popülerdir. Ne olduğuna baktığımızda programcı olarak işimizi yapabilmemiz için çok kısıtlı sayıda araç ihtiva ettiğini görmekteyiz. Gerçek dünyayı modellemek için sınıfları, bu modellere hareket kabiliyeti verebilmek icin metotları kullanırız. Koca dünyayı modellemek için sadece bu iki aracı kullanabiliyoruz. Kod yazarken yapmak istediklerimizi ifade etmek için if ya da while gibi basit komutları kullanıyoruz. Kullanabileceğimiz başka ne var ki? Bu araçlardan faydalanıp nasıl bir sanat eseri ortaya koyabiliriz? Bu bir sanat eseri olsa bile fonksiyonel işlevi haricinde nesi kimi ilgilendirir?

Görüldügü gibi programcı sahip olduğu araçlar itibari ile çok kısıtlı bir dünyada yaşamaktadır. Oysaki bir sanatçının dünyası rengarenk ve pırıl pırıldır. Sayısız derecede kombinasyon imkanları vardır. Bunlardan faydalanarak sanatını icra eder ve sanat eserleri oluşturur. Biz programcılar sıkışıp kaldığımız mantıksal dünyada bu araç gerece ve lükse sahip degiliz.

Kanımca programcılıkta kreatif olma, sanat icrası ile karıştırılmaktadır. Programcılar kreatiftir. Devamlı karşılaştıkları sorunları aşmak için çözümler üretirler. Bunun için bir zanaatkar usta gibi bir takım alet, edevata ihtiyaç duyarlar. Usta programcılar örneğin tüm tasarım prensiplerine hakımdirler. Nerede hangi tasarım şablonunu kullanmaları gerektiğini bilirler. Az ve öz kod yazmayı yeğlerler. Yazdıkları kod roman gibi okunur, kod okundukça bir hikaye gibi kendisini anlatır. Burada bir tutam estetiklik yok değildir. Ama yinede bu aktivitelerin hiçbirisi bir sanat eseri ortaya koymaz.

Programcılık mantık işidir. Biz programcılar mantığın kölesiyiz. Programcılıkta bütün gidişati mantık yönetir. Mantığın bittiği yerde programcılık biter. Bir sanatçı hiçbir şeyin kölesi değildir. Aradaki fark budur. Mantıkla sanatı kavramaya çalışmak beyhude bir iştir.


EOF (End Of Fun)
Özcan Acar

Share/Bookmark

Kurumsal Java Yazılımı - Özcan Acar

unread,
May 17, 2012, 10:39:34 AM5/17/12
to kurumsaljava+...@googlegroups.com

Çöplerin Efendisi

Posted: 17 May 2012 03:56 AM PDT

Java programcısının çok sadık bir hizmetçisi var. Her türlü çöplüğü, pisliği arkasından devamlı toplar, hiç sesini çıkarmaz. Çöplerin efendisidir, ama bir o kadar da mütevazidir. Kimseye belli etmeden işini görür. Bu yüzden birçok Java programcısı onun farkında bile değildir. Ama o işini yapmasa Java programcısının hali çok vahim olur, C/C++ ile kod yazan meslektaşlarından bir farkı kalmaz, bilgisayarın hafızası denilen kara delikte kaybolur gider, yazdığı programlar devamlı sallanır.

Garbage Collector’dan bahsediyorum. Çoğu Java programcısının varlığından bile haberdar olmadığı, bazılarının çekindiği, bazılarının ne yaptığını tam olarak anlamdığı, bazılarının ise yaptığı işe hayran olup, ince ince hayranlık duyduğu Garbage Collector… Java’yı Java yapan özelliklerinden bir tanesi bir Garbage Collector’e sahip olmasıdır. Şimdi yakından tanıyalım bu kahramanı.

Java dilinde herhangi bir sınıftan bir nesne oluşturmak için new operatörü, Class.forName().newInstance(), clone() veya readObject() metodu kullanılır. Örneğin new operatörü sınıfın konstrüktörünü kullanarak JVM bünyesinde yeni bir nesne oluşturur. Nesne inşa edildikten sonra Java Heap içinde konuşlandırılır. Nesnelerin hepsi kullanıldıkları sürece heap icinde mutlu, mesut hayatlarını sürdürürler. Ama her canlı gibi onlar da birgün ölürler. Öldüklerinde onları toplayıp, ebediyete intikal ettiren Garbage Collector’dür.

İngilizce’de garbage çöp anlamına gelmektedir. Garbage Collector JVM bünyesinde hem yeni nesnelerin doğmasına yardımcı olan, hem de ölen nesneleri ortadan kaldıran modüldür. new operatörü ile doğan yeni nesneler için Garbage Collector hafıza alanını temin eder. Ölen ve çöp olarak isimlendirilen nesnelerin işgal ettikleri hafıza alanını Garbage Collector boşaltır ve yeni nesnelere tayin eder. Garbage Collector kısaca Java’da otomatik hafıza yönetiminden sorumludur. Onsuz Java’nın C/C++ dilinden bir farkı kalmazdı.

Garbage Collector’ün nasıl çalıştığını görebilmek için aşağıda yer alan videoyu hazırladım. Collector’ü kızdırmak için bolca çöp (garbage) üreten minik bir Java programını şu şekilde oluşturmak yeterli:


package com.kurumsaljava.jvm.garbage;

import java.util.ArrayList;
import java.util.List;

public class Heap {

	public static void main(String[] args) throws Exception {
		List<String> temp = new ArrayList<String>();

		Thread.sleep(10000);
		while (true) {

			temp.add("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
			//System.out.println(temp.size());

			if(temp.size() > 3000000) {
				temp = null;
				temp = new ArrayList<String>();

			}
		}
	}
}

Heap ismini taşıyan sınıf bünyesinde x’lerden oluşan uzunca bir String nesnesini sonsuz bir döngü içinde temp ismindeki bir listeye yerleştiriyoruz. Liste bünyesinde yer alan String nesne adedi 300.000′e ulaştığında listeye null değerini atayarak siliyor ve yeniden bir liste oluşturuyoruz. Döngü içinde tekrar String nesneleri yeni listeye ekleniyor. Buradaki amacım hafızanın tükenmesi ile java.lang.OutOfMemoryError oluşmasını engellemek ve Garbage Collector’ün değişik heap alanları üzerinde nasıl çalıştığını gösterebilmekti. Java’da herhangi bir nesneye null degeri atandığında Garbage Collector’e “bu nesne ölmüştür, yok edilebilir” mesajı verilmektedir. Eğer bu nesneye herhangi başka bir nesneden referans yoksa, Garbage Collector bu nesneyi öldü kabul edip, temizleyecektir. Şimdi beraber videoyu izleyelim.

Videoda yer alan program için 64 MB büyüklüğünde bir heap alanını -Xms64m -Xmx64m JVM parametreleri kullanarak tahsis ettim. Eğer heap alanı 10 MB olursa OutOfMemoryError hatası oluşmaktadır. Aynı şekilde liste içindeki String nesne adedini 600.000′e çıkardığımda hafıza yetersizliğinden dolayı OutOfMemoryError hatası oluşmaktadır.

Video ve oluşan Garbage Collection aktiviteleri hakkında detaya girmeden önce, değişik Garbage Collector mekanizmalarını ve algoritmalarını gözden geçirelim.

Generational Garbage Collection

Tipik bir Java uygulaması gözlemlendiğinde, uygulamanın hayatı süresince oluşan birçok nesnenin kısa ömürlü oldukları görülmektedir. Oluşturulan nesnelerin çogu bir metot son bulduktan sonra, sadece metot bünyesinde kullanıldıkları için hayata gözlerini yumarlar. Bunun yanısra bir Java uygulaması bünyesinde nesneler yaşlanarak belli bir yaşa erişebilirler. Bir nesnenin yaşlanma süreci uygulama bünyesinde kullanılma derecesini yansıtmaktadır.


Resim 1

Java uygulamarında oluşturulan nesnelerin hayat döngülerini yönetmek icin Sun firması tarafından JDK 1.3 versiyonu ile Generational Garbage Collection geliştirilmiştir. Resim 1’de görüldüğü gibi Generation Garbage Collection ile Java uygulamasının kullandığı hazıfa iki jenerasyona bölünmektedir. Yeni doğan nesneler genç jenerasyon (young generation), orta ve ileri yaşlı nesneler yaşlı jenerasyon (old generation) bölümünde konuşlandırılırlar.

Nesnelerin ait oldukları yaş gurubuna gore ihtiyaç duyulan hafıza yönetim mekanizmaları farklılık gösterir. Yeni jenerasyon bünyesinde nesne doğum ve ölümleri çok hızlı gerçekleşir. Bu bölümde kullanılan Garbage Collection algoritmasının yeni doğan nesnelere hızlı bir şekilde hazıfa alanı tahsis etmesi, ölen nesneleri ortadan kaldırması gerekmektedir. Bu jenerasyon için kullanılan Garbage Collection algoritması bu ihtiyaca cevap verecek şekilde geliştirilmiştir. Yaşlı jenerasyonda durum farklıdır. Bu bölümde Formel 1’de olduğu gibi hızlı haraket etme gerekliliği yoktur. Bu jenerasyonda nesneler daha yavaş bir hayat temposuna sahiptir. Bu jenerasyonda kullanılan Garbage Collection algoritması buna uygun şekilde geliştirilmistir.

Young Generation Garbage Collection

Young generation üç hafıza alanından oluşmaktadır. Bunlar Eden, Survivor 1 ve Survivor 2 hafıza alanlarıdır. Bir Java uygulamasında nesnelerin hepsi gözlerini Eden hafıza alanında dünyaya açarlar. Survivor 1 ve Survivor 2 alanları geçici ara hafıza alanı olarak kullanılır. Eden hafıza alanı dolduğunda, Garbage Collector hayatta kalan nesneleri önce boş olan Survivor alanlarından birisine kopyalar. Bu işlemin ardından Garbage Collector Eden ve kullanımda olan Survivor alanını boşaltır. Eden hafıza alanı tekrar dolduğunda, hayatta kalan nesneler ve dolu olan Survivor alanındaki nesnelerin hepsi tekrar boş olan Survivor alanına kopyalanır. Her zaman bir Survivor alanı boştur ve bir sonraki Garbage Collection işlemi sonunda hayatta kalan nesneleri bünyesine alacak şekilde pasif olarak bekler. Videoda da Eden ve Survivor hafıza alanlarının Garbage Collector tarafından doldurulma ve boşaltılma işlemleri görülmektedir. Young generation bünyesinde çalışan Garbage Collection mark & copy (işaretle ve kopyala) algoritmasını kullanmaktadır. Şimdi bu algoritmanın nasıl çalıştığını yakından inceleyelim.

Mark & Copy Algoritması

JVM bünyesinde kullanılan tüm Garbage Collection mekanizmaları hayattaki nesneleri işaretleme (mark) işlemi ile işe başlarlar. Garbage Collector hangi nesnelerin hayatta olduğunu anlamak için kök setini (root set) oluşturan nesnelerden yola çıkıp, diğer nesnelere olan bağlantıları takip eder. Bu işlemi bir ağacın kökünden yola çıkarak, dallarına kadar erişme olarak düşünebiliriz. Garbage Collector bu şekilde hayatta olan tüm nesneleri işaretler. Erişemediği diğer nesnelerin hepsini ölü kabul eder ve bir sonraki Garbage Collection ile ortadan kaldırılmalarını sağlar. Kök setinde kendisine doğru nesne referansı olmayan, ama kendisinden başka nesnelere referans giden nesneler yer alır. Bu tip nesneler genelde metot parametreleridir ya da global geçerliliği olan statik olarak tanımlanmış nesnelerdir.


Resim 2

Resim 2’de kök setin A ve E nesneslerinden oluştuğu görülmektedir. Garbage Collector işaretleme işine önce A nesnesinden yola çıkarak başlar. B, C ve dolaylı olarak D nesnelerine erişir ve bu nesne kümesini (A,B,C,D) hayatta olan nesneler olarak işaretler. İşaretleme işine E nesnesi ile devam eder. E nesnesi kök setinde yer alan bir nesnedir, lakin E’den yola çıkarak başka bir nesneye erişmek mümkün değildir. Ayrıca başka bir nesne de E nesnesine işaret etmemektedir. Bu yüzden E nesnesi artık ölmüş bir nesnedir ve bir sonraki Garbage Collection ile E nesnesinin işgal ettiği hafıza alanı temizlenir.

Garbage Collector işaretleme (mark) işlemini tamamladıktan sonra hayatta kalan nesneleri aktif olan Survivor alanına kopyalama işlemine başlar. Garbage Collector resim 2’de görüldüğü gibi hayatta kalan nesne kümesini (ABCD) aktif olan Survivor 1 alanına kopyalar. Nesneler, işgal ettikleri adres alanları arka arkaya gelecek şekilde Survivor 1’de konuşlandırılır. Garbage Collection işlemi sonunda hayatta kalan nesnelerin birbirlerine olan referansları ve sahip oldukları adres alanları değişeceği için Garbage Collector işaretleme ve kopyalama işlemi öncesinde uygulama bünyesinde çalışan tüm threadleri durdurmak zorundadır. Bu işlem JVM terminolojisinde dünyayı durdurma (stop the world) olarak adlandırılır. Belli bir süre boyunca uygulama durur ve uygulama bünyesinde aktif olan sadece Garbage Collector’dür. Eğer Garbage Collector işine başlamadan önce dünyayı durdurmamış olsa, aktif olan uygulama threadleri nesne referansları üzerinde değişiklik yaparak, Garbage Collector’ün yanlış nesneler üzerinde işlem yapmasına sebep olabilirler. Bu sebepten dolayı Garbage Collection öncesi dünyanın durması gerekmektedir.


Resim 3

Resim 3’de Garbage Collection sonrasınra Eden bünyesinde yeni nesnelerin oluştuğu görülmektedir. Kök set içinde X ve W nesnesi yer almaktadır. Garbage Collector X,Y,Z ve Survivor 1’de yer alan ABCD nesnelerini Survivor 2’ye kopyalar. Garbage Collection işlemi sonunda young generation hafıza alanındaki son durum resim 4’de yer almaktadır.


Resim 4

Görüldüğü gibi her Garbage Collection sonunda Survivor alanları rolleri değiştirmektedir. Survivor alanlarının her Garbage Collection sonrası yer degiştirmeleri resim 5’de de görülmektedir.


Resim 5

Resim 5’de yer alan birinci kolon Survivor 1’in, ikinci kolon Survivor 2’nin, üçüncü kolon Eden’in, dördüncü kolon Old hafıza alanının doluluk oranını göstermektedir. Görüldüğü gibi Garbage Collector düzenli olarak hayatta kalan nesneleri kopyalamak için pasif olan Survivor alanını kullanmaktadır.


Resim 6

Bir Java uygulaması -XX:+PrintHeapAtGC JVM parametresi ile çalıştırıldığv zaman, resim 6’da yer alan Garbage Collection log kayıtları oluşmaktadır. İlk kırmızı işaretli alan içinde Survivor 1 from, Survivor 2 ise to olarak isimlendirilmiştir. Survivor 1’in hafıza başlangıç adresi 0×30e40000, Survivor 2’nin hafıza başlangıç adresi 0×30c20000’dir. İkinci kırmızı işaretli alana baktığımızda from ve to’nun adres alanlarının değiştiğini görmekteyiz. Garbage Collection işlemi oluşmuş ve Survivor alanları yer degiştirmiştir. Garbage Collector tarafından hep bir Survivor alanı reserve durumunda bir sonraki Garbage Collection içinde kullanılmak üzere beklemede tutulur. Bu dogal olarak belirli bir hafıza alanının kullanılmayarak israf edildiği anlamına gelir. Ne yazık ki bu Mark & Copy algoritmasının bir dejavantajıdır. Bunun haricinde bu algoritma hızlı bir şekilde Eden bünyesinde olup bitenlere cevap verebilecek yetenekte ve kapasitededir.

Minor & Major Garbage Collection

Garbage Collection minor (küçük) ve major (büyük) olmak üzere ikiye ayrılır. Minor Garbage Collection’ı kendi odamızı, Major Garbage Collection’ı evimizi temizleme gibi düşünebiliriz. Young Generation bünyesinde yapılan Garbage Collection işlemine minor Garbage Collection, JVM kontrolündeki tüm hafıza alanlarının temizlenme işlemine major Garbage Collection ismi verilir.


Resim 7

Resim 7’de Garbage Collector tarafından yapılan minor ve major Garbage Collection işlemleri yer almaktadır. GC ile başlayan satırlar minor, Full GC ile başlayan satırlar major Garbage Collection yapıldığınin ibaresidir. Major Garbage Collection işlemi için ayrıca Full Garbage Collection ismi de kullanılmaktadır.

İlk satırda young generation tarafından işgal edilen hafıza alanı toplamda 11994 kilobytetır (~ 12 MB). Minor Garbage Collection işlemi sonunda ölen nesneler temizlenmiş ve hayatta kalan nesnelerin kapladığı alan 4048 (~ 4 MB) kilobyte olarak değişmiştir. Garbage Collector minor Garbage Collection işlemi ile 8 MB civarında ölü nesneyi hafıza alanından kaldırmıştır. Bu minor Garbage Collection işlemi 0.02 saniye sürmüştür. Bu zaman diliminde JVM bünyesinde sadece Garbage Collection işlemi yapılmıştır. Parantez içinde yer alan 63360K (~ 64 MB) JVM tarafından kullanılan tüm hafıza alanlarının toplamını yansıtmaktadır. JVM parametreleri olarak -Xms64m -Xmx64m kullandığımız için, programın işgal ettiği toplam hafıza alanı 64 MB’dir.

Eden hafıza alanı, Old hafıza alanına göre daha küçük olduğu için bu alanda minor Garbage Collection işlemleri Old generation bünyesinde olan major Garbage Collection işlemlerinden sayıca çok daha fazladır. Resim 7’de Garbage Collector 20 minor Garbage Collection, buna karşılık sadece 4 major Garbage Collection gerçekleştirmiştir.


Resim 8

Yedinci satırda major Garbage Collection yapıldığını görmekteyiz. Tüm JVM bünyesinde 56241 kilobyte (~56 MB) hafıza alanı kullanımdadır (young + s0 + s1 + old). Görüldüğü gibi bu değer JVM hafıza büyüklüğü olan 64 MB’ye yakın bir değerdir. Resim 8’de young ve old generation hafıza alanlarının dağılım oranları yer almaktadır. JVM Eden için 17 MB, S0 için 2 MB, S1 için 2 MB ve Old icin 42 MB alan ayırmıştır. JVM parametreleri ile hafıza alanlarının büyüklüklerini değiştirmek mümkündür. Buna yönelik bir ayarlama yapılmadığında JVM otomatik olarak hafıza alanlarının büyüklüğünü ayarlar. Full GC ardından hayatta kalan nesnelerin kapladığı alan 6011 kilobyte (~6MB) olarak değişmiştir. Bu major garbage collection işlemi ile 50 MB büyüklüğünde nesneler Garbage Collector tarafından ebediyete uğurlanmıştır.

Durdurun Dünyayı

“Durdurun dünyayı başım dönüyor” diyor Ferdi Tayfur şarkısında. JVM mucitleri Ferdi Tayfur’un bu şarkısından esinlenerek dünyayı durduran Garbage Collector algoritmaları geliştirmişlerdir. Arabesk müziğin Java üzerindeki etkilerini başka bir yazımda detaylı olarak ele alacağım. Arabeskin programcılık üzerinde etkileri düşünüldüğünden çok daha fazladır. (:)

Garbage Collector minor Garbage Collection için tek bir thread (Reaper Thread) kullanmaktadır. Garbage Collection işlemi esnasında Garbage Collector JVM bünyesinde çalışan tüm threadleri durdurur. Uygulama için dünya durmuş olur. Dünya ne kadar uzun durursa, uygulamanın kullanıcıya verdiği cevaplar o oranda gecikir. Buradan Garbage Collection işlemi esnasında verilen araların mümkün olduğunca kısa olması gerektiği sonucu çıkmaktadır. JVM mucitleri bunu göz önünde bulundurarak paralel çalışan minor Garbage Collection algoritması geliştirmişlerdir.

Paralel çalışan minor Garbage Collection algoritması, tekil threadle işini gören minor Garbage Collection ile teoride aynı yapıdadır. Aralarındaki tek fark, parelel minor Garbage Collection için birden fazla threadin çalışmasıdır. Bu şekilde stop the world duraklamaları daha kısaltılmış olur. Bu uygulamanın kullanıcıya karşı cevapkanlık oranını artırmaktadır.

Paralel minor Garbage Collection nasıl çalışmaktadır? Garbage Collector’ün temizleme işlemine hayatta kalan nesneleri işaretleyerek (mark) başladığını yazmıştım. Garbage Collector bu amaçla kök setinde yer alan nesneleri tarar ve bu nesnelerden yola çıkarak işaretleme işlemini yapar. Akabinde hayatta kalan tüm nesneleri aktif olan Survivor alanına kopyalar (copy). Paralel Minor Garbage Collection için kök setinde yer alan nesneler birden fazla Garbage Collection threadine atanır. Birden fazla thread paralel olarak kök setindeki bir nesneden yola çıkarak hayatta kalan nesneleri tararlar. Bu şekilde işaretleme performansı artırılır ve işaretleme için harcanan zaman azalır.

Hayatta kalan nesnelerin aktif olan Survivor alanına da kopyalanma işlemi buna benzer bir şekilde gerçekleşir. Her threade Survivor hafıza alanında PLAB (PLAB – Parallel Local Alocation Buffer) ismi verilen bir alan atanır. Garbage Collection threadleri birbirlerinden bağımsız olarak keşfettikleri ve hayatta kalmış olan nesneleri kendi PLAB alanlarına kopyalarlar.

Paralel Garbage Collection paralel çalışan threadlere rağmen dünyayı durduran bir algoritmadır. Tekil threadle çalışan Garbage Collection algoritmasına göre daha hızlı çalışır ve uygulamanın durma zamanlarını kısaltır. Birden fazla işlemcisi (CPU – Central Processing Unit) olan bir sunucu üzerinde paralel Garbage Collection algoritmasını kullanmak bu kazancı getirir. Lakin sadece bir işlemcisi olan sunucularda paralel Garbage Collection’ın kullanılması tam tersi bir etki yaratır.

Yazımın ikinci bölümünde major Garbage Collection’ın nasıl çalıştığını inceleyeceğim.

EOF (End Of Fun)
Özcan Acar

Share/Bookmark
Reply all
Reply to author
Forward
0 new messages