Java'da nesne başlatma “Foo f = new Foo ()” esasen C'de bir işaretçi için malloc kullanmakla aynı mıdır?


9

Java'da nesne oluşturma işlemlerinin ardındaki gerçek süreci anlamaya çalışıyorum - ve diğer programlama dillerini varsayalım.

Java'da nesne başlatmanın, C'deki bir yapı için malloc kullandığınızla aynı olduğunu varsaymak yanlış olur mu?

Misal:

Foo f = new Foo(10);
typedef struct foo Foo;
Foo *f = malloc(sizeof(Foo));

Bu yüzden nesnelerin yığın yerine yığın üzerinde olduğu söylenir mi? Aslında verilere sadece işaretçiler oldukları için mi?


Nesneler öbek üzerinde c # / java gibi yönetilen diller için oluşturulur. Cpp'de yığın üzerinde de nesneler oluşturabilirsiniz
bas

Neden Java / C # yaratıcıları nesneleri yalnızca yığın üzerinde saklamaya karar verdiler?
Jules

Ben basitlik uğruna düşünüyorum . Nesneleri yığına depolamak ve daha derin bir düzeye taşımak nesneyi yığına kopyalamayı içerir, bu da kopya oluşturucuları içerir. Ben doğru bir cevap için google yoktu, ama eminim kendinize daha tatmin edici bir cevap bulabilirsiniz (ya da başka biri bu yan soru üzerinde ayrıntılı bir şekilde)
bas

@Java'daki jules nesneleri, çalışma zamanında (çağrılan scalar-replacement) yalnızca yığın üzerinde yaşayan düz alanlara "ayrıştırılabilir" ; ama bu böyle bir şey JITdeğil javac.
Eugene

"Yığın", yalnızca ayrılmış nesneler / bellekle ilişkilendirilmiş bir dizi özellik için bir addır. C / C ++ 'da “yığın” ve “yığın” olarak adlandırılan iki farklı özellik kümesi arasından seçim yapabilirsiniz, C # ve Java'da, tüm nesne ayırma işlemleri, belirtilen "yığın" adı altında aynı belirtilen davranışa sahiptir. bu özelliklerin C / C ++ “yığın” ile aynı olduğunu ima eder, aslında değildir. Bu, uygulamaların nesneleri yönetmek için farklı stratejilere sahip olamayacağı anlamına gelmez, bu stratejilerin uygulama mantığıyla ilgisiz olduğu anlamına gelir.
Holger

Yanıtlar:


5

C de malloc()öbekteki bir bellek bölgesini tahsis eder ve ona bir işaretçi döndürür. Tüm elde ettiğiniz bu. Bellek başlatılmaz ve bunların tamamen sıfır veya başka bir şey olduğunu garanti etmezsiniz.

Java'da arama new, yığın tabanlı bir ayırma gibi yapar malloc(), ancak ayrıca bir ton ek kolaylık elde edersiniz (veya isterseniz ek yük). Örneğin, ayrılacak bayt sayısını açıkça belirtmeniz gerekmez. Derleyici, ayırmaya çalıştığınız nesnenin türüne göre bunu sizin için bulur. Ayrıca, nesne yapıcıları çağrılır (başlatmanın nasıl gerçekleştiğini denetlemek isteyip istemediğinizi bağımsız değişkenlere iletebilirsiniz). Geri newdöndüğünde, başlatılmış bir nesneye sahip olmanız garanti edilir.

Ama evet, çağrı hem sonucu sonunda malloc()ve newsadece yığın tabanlı verilerin bazı yığın göstericisidir.

Sorunuzun ikinci kısmı, bir yığın ile yığın arasındaki farkları sorar. Derleyici tasarımı hakkında bir ders alarak (veya hakkında bir kitap okuyarak) çok daha kapsamlı cevaplar bulunabilir. İşletim sistemleri üzerine bir kurs da yardımcı olacaktır. Ayrıca yığınlar ve yığınlar hakkında SO üzerinde çok sayıda soru ve cevap vardır.

Bunu söyledikten sonra, umarım çok ayrıntılı değildir ve farklılıkları oldukça yüksek bir düzeyde açıklamayı amaçlamaktadır.

Temel olarak, iki bellek yönetim sistemine sahip olmanın temel nedeni, yani bir yığın ve yığın, verimlilik içindir . İkincil bir sebep, her birinin belirli tür problemlerde diğerinden daha iyi olmasıdır.

Yığınlar kavram olarak anlamam biraz daha kolay, bu yüzden yığınlarla başlıyorum. Bu işlevi C dilinde ele alalım ...

int add(int lhs, int rhs) {
    int result = lhs + rhs;
    return result;
}

Yukarıdakiler oldukça basit görünüyor. add()Sol ve sağ ekler adında bir işlev tanımlar ve iletiriz. İşlev bunları ekler ve bir sonuç döndürür. Lütfen oluşabilecek taşmalar gibi tüm uç durumları göz ardı edin, bu noktada tartışma konusu değildir.

add()İşlevin amacı oldukça basit görünüyor, ama biz onun ömrü ile ilgili ne söylersin? Özellikle bellek kullanımı ihtiyacı var mı?

En önemlisi, derleyici , veri türlerinin ne kadar büyük olduğunu ve kaç tanesinin kullanılacağını önceden bilir (yani derleme zamanında). lhsVe rhsbağımsız değişkenler olarak sizeof(int)4 adet bayt. Değişken resultde sizeof(int). Derleyici, add()işlevin 4 bytes * 3 intsveya toplam 12 bayt bellek kullandığını söyleyebilir .

Zaman add()fonksiyonu olarak adlandırılır, bir donanım yazmacı adı yığın işaretçisi yığının üstüne noktalarının içinde bir adres vardır. add()İşlevin çalışması için gereken belleği ayırmak için , tüm işlev giriş kodu, yığın işaretçisi kayıt değerini 12 oranında azaltmak için tek bir montaj dili talimatı verir. Bunu yaparken, yığın üzerinde üç kişilik depolama alanı oluşturur. intsher biri bir için lhs, rhsve result. Tek bir komut uygulayarak ihtiyacınız olan bellek alanını elde etmek, hız açısından büyük bir kazançtır çünkü tek komutlar bir saat işareti (1 GHz CPU saniyede 1 milyarda biri) yürütme eğilimindedir.

Ayrıca, derleyicinin görünümünden, bir dizini dizine eklemek gibi çok fazla görünen değişkenlere bir harita oluşturabilir:

lhs:     ((int *)stack_pointer_register)[0]
rhs:     ((int *)stack_pointer_register)[1]
result:  ((int *)stack_pointer_register)[2]

Yine, tüm bunlar çok hızlı.

Ne zaman add()fonksiyonu çıkar o temizlemek zorundadır. Bunu, yığın işaretçisi kaydından 12 bayt çıkararak yapar. Bir çağrıya benzer, free()ancak yalnızca bir CPU talimatı kullanır ve sadece bir onay alır. Çok, çok hızlı.


Şimdi yığın tabanlı bir ayırmayı düşünün. Ne kadar belleğe ihtiyacımız olacağını önceden bilmediğimizde bu devreye girer (yani yalnızca çalışma zamanında öğreneceğiz).

Bu işlevi göz önünde bulundurun:

int addRandom(int count) {
    int numberOfBytesToAllocate = sizeof(int) * count;
    int *array = malloc(numberOfBytesToAllocate);
    int result = 0;

    if array != NULL {
        for (i = 0; i < count; ++i) {
            array[i] = (int) random();
            result += array[i];
        }

        free(array);
    }

    return result;
}

addRandom()Fonksiyonun derleme sırasında countargümanın değerinin ne olacağını bilmediğine dikkat edin . Bu nedenle array, yığını yığına koyarsak, bizim gibi tanımlamaya çalışmak mantıklı değil :

int array[count];

Eğer countbüyük bizim yığını çok büyük ve üzerine yazma diğer program segmentlerini büyümeye neden olabilir. Bu yığın taşması gerçekleştiğinde programınız çöker (veya daha kötüsü).

Bu nedenle, çalışma zamanına kadar ne kadar belleğe ihtiyacımız olacağını bilmediğimiz durumlarda kullanırız malloc(). Daha sonra ihtiyacımız olduğunda ihtiyacımız olan bayt sayısını isteyebiliriz ve malloc()o kadar bayt satabildiğini kontrol edeceğiz. Mümkünse, harika, geri alıyoruz, eğer değilse, bize malloc()başarısız olma çağrısını söyleyen bir NULL işaretçisi alırız . Özellikle, program çökmez! Elbette programcı olarak, kaynak tahsisi başarısız olursa programınızın çalışmasına izin verilmeyeceğine siz karar verebilirsiniz, ancak programcı tarafından başlatılan sonlandırma sahte bir çökmeden farklıdır.

Şimdi verimliliğe bakmak için geri dönmeliyiz. Yığın ayırıcı süper hızlıdır - tahsis edilecek bir talimat, dağıtmak için bir talimat ve derleyici tarafından yapılır, ancak yığının bilinen büyüklükteki yerel değişkenler gibi şeyler için tasarlandığını unutmayın, bu nedenle oldukça küçük olma eğilimindedir.

Öte yandan, öbek ayırıcısı birkaç kat daha yavaştır. Kullanıcının istediği bellek miktarını satabilmek için yeterli boş belleğe sahip olup olmadığını görmek için tablolarda bir arama yapmalıdır. Kimsenin bu bloğu kullanamayacağından emin olmak için belleği çıkardıktan sonra bu tabloları güncellemesi gerekir (bu defter tutma, ayırıcıdan satmayı planladığı şeyin yanı sıra kendisinin de bellek ayırmasını gerektirebilir ). Ayırıcı, belleği güvenli bir şekilde geçirdiğinden emin olmak için kilitleme stratejileri kullanmalıdır. Ve hafıza sonundafree()Farklı zamanlarda gerçekleşen ve öngörülebilir bir sırada olmayan, d: ayırıcı, bitişik bloklar bulmak ve yığın parçalanmasını onarmak için bunları birleştirmek zorundadır. Bunların hepsini gerçekleştirmek için tek bir CPU komutundan daha fazlasını alacak gibi görünüyorsa, haklısınız! Çok karmaşık ve biraz zaman alıyor.

Ancak yığınlar büyük. Yığınlardan çok daha büyük. Onlardan çok fazla bellek alabiliriz ve derleme zamanında ne kadar belleğe ihtiyacımız olacağını bilmediğimizde harika olurlar. Bu yüzden, çok büyük bir şey ayırmaya çalıştığımızda çökmek yerine bizi kibarca reddeden yönetilen bir bellek sistemi için hızdan ödün veriyoruz.

Umarım bu bazı sorularınızı yanıtlamanıza yardımcı olur. Yukarıdakilerden herhangi biri hakkında açıklama yapmak istiyorsanız lütfen bize bildirin.


int64 bit platformda 8 bayt değil. Hala 4'tür. Bununla birlikte, derleyicinin intyığının üçüncü dışını dönüş yazmacına optimize etme olasılığı çok yüksektir . Aslında, iki argümanın herhangi bir 64 bit platformda yazmaçlarda olması muhtemeldir.
SS Anne

Cevabımı int64-bit platformlarda 8 baytlık ifadeyi kaldırmak için düzenledim . intJava'da 4 bayt kalan doğru . Ancak cevabımın geri kalanını bıraktım çünkü derleyici optimizasyonuna girmenin arabayı atın önüne koyduğuna inanıyorum. Evet, bu noktalarda da haklısınız, ancak soru yığınlar ve yığınlar hakkında açıklama istiyor. RVO, kayıtlardan geçen argüman, kod seçimi, vb. Temel kavramları aşırı yükler ve temelleri anlama yoluna girer.
par
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.