pasa daha fazla koç nasıl tahsis edilir


cevap 1:

Kısacası, yığın basit ve hızlıdır, ancak boyut sınırlıdır ve tahsis yalnızca işlevin sonuna kadar sürecektir. Yığın çok daha büyüktür, uzun vadeli ve büyük bellek yığınlarını ayırmak için kullanılabilir ve daha fazla yönetim gerektirir, bu nedenle ayırmak oldukça yavaştır.

Yığın için neden bellek ayırma / serbest bırakma gerekmiyor?

Genellikle "yığın" olarak adlandırılan çağrı yığını, işlev çağrısının önemli bir parçasıdır. Yığın değişkenleri ve çağrının mevcut durumunu korumak için kullanılır.

Genel olarak, yığın için kullanılan bellek, işlemin / iş parçacığının başlangıcında belirli bir sabit boyutla tahsis edilir ve ömrü boyunca kullanılır. Bu nedenle "bellek tahsisine" gerek olmadığı söylenir. Aslında, aynı zamanda

bir çeşit bellek tahsisi

, ancak yığındaki ayırma, yığın tabanlı ayırmaya kıyasla çok basit ve çok hızlıdır, bu nedenle "ayırma" gerekmediğini söyleme eğilimindeyiz.

Ayrıca, derleyici nesnenin yığında nerede olacağını neden bilemiyor?

Bunu söyleyerek, yığının nasıl çalıştığını zaten biliyor olabilirsiniz. Yığın yığın gibi çalışmıyor. Yığın, esasen, her bir bölümün belirlenmemiş bir sırayla tahsis edilebildiği ve ayrılabildiği bir grup bellektir. Bunu, her bir yolcunun belirli sayıda bitişik bojiyi işgal edebileceği çok uzun (ancak sınırlı uzunlukta) bir tren gibi düşünün. Yolcular, herhangi bir plan veya düzen olmaksızın istasyonlar arasında inecek ve inecek. Yığın yönetimi, bu yolcuları ihtiyaç duydukları boji miktarını alacak şekilde yönetmeye benzer, böylece trene binebilirler, bu da mevcut tüm bojilerin izlenmesini ve bitişik bojiler tükenmeden onlar için en uygun bojileri bulmak için bir algoritma gerektirir. uzun vadede. Bu, bazı verileri çağrı yığınına koymaktan çok daha karmaşık ve zaman alıcıdır.

Derleyici nesnenin yığın halinde depolanırsa nerede olacağını nasıl bilebilir?

Çağrı yığınına ait olan yeni nesneler her zaman çağrı yığınının üstünde saklanacaktır. Bu çok optimize edilmiştir çünkü yığın işlemleri, tümünde değilse de çoğu CPU tipinde yerel olarak desteklenir ve "yığın işaretçisi" için en az bir adanmış dahili kayıt sağlar - çağrı yığınının tepesine işaret eden işaretçi. Derleyici aslında nesnenin yığında nerede saklanacağını bilmez, ancak bunu yapmak için kesinlikle talimat üretebilir.

Büyük bir uyarı, yığının çoğu durumda yığından daha hızlı olduğundan emin olsak da, burada tahsis başına nanosaniye farkından bahsediyoruz. Önemsiz bir işlev için bu tür bir optimizasyonu yapmaya çalışmayın.


cevap 2:

Yığının ne olduğunu ve yığının ne olduğunu basit bir İngilizce ile açıklamaya çalışalım.

Yığın, bir işlev çağrıldığında oluşturulan bir bellek bloğu yığınıdır. Bu nedenle, bir C ++ programı bir işlevi her çağırdığında (herhangi bir işlev. Hatta main ()), bu işlev için yığın üzerinde bir bellek bloğu ayırır.

Bu bellek bloğu, bu işlevde oluşturulan her değişkeni ve onu çağıran orijinal işlevin adresini içerir. Bu işlevin yürütülmesi sona erdiğinde, bu bellek bloğunu otomatik olarak serbest bırakır, işlevin statik olarak oluşturduğu değişkeni bile (herhangi bir int, char, float, her neyse) etkin bir şekilde siler. Bu nedenle, derleyiciler, oluşturulan her bir değişkenin belleğe nereye yerleştirileceğini önceden bildiğinden, birçok optimizasyon yapabilir.

Dinamik değişkenler oluşturmak istemiyorsanız. İşaretçi gibi şeyler (veya bir vektör . Bu durumda, elemanlar örneğin yığın üzerinde tahsis edilmeyecektir. Şu durumu hayal edelim (bu basit C, ancak açıklama uğruna işe yarayacak):

void function (int m) { int * v = (int *) malloc (m * sizeof (int)); // onunla bir şeyler yap}

Kodu okuyamıyorsanız, * v işaretçisi kullanarak m boyutunda bir tamslar dizisi oluşturur. Bu durumda, derleyicinin dizinin boyutunu önceden bilmesinin bir yolu yoktur, çünkü m yalnızca program gerçekten çalışırken bilinecektir. Öyleyse, derleyici çağrıldığında yığındaki bu işlev için bellek bloğunun ne kadar büyük olması gerektiğini nasıl bilir?

Cevap şu: Değil. Bu durumda, (ve aslında, std :: vector'da olduğu gibi, örtük olarak da olsa, işaretçiler kullandığınız herhangi bir durumda ), gerçek dizi yığında değil, yığında, yani yığından bağımsız olarak belleğin farklı bir bölgesinde depolanır. Bu nedenle, derleyici yalnızca POINTER için yeterli bellek ayırır, veriler için değil (bu durumda, muhtemelen 32 bitlik bir tam sayıdır) ve verilerin kendisi, işaretçide depolanan adresle belleğin farklı bir bölümünde yaşar ( etrafındaki sadelikçiler için, işaretçi dizinin ilk elemanının adresidir). Bununla ilgili sorun, işlev geri döndüğünde, dizinin kendisi hakkında herhangi bir bilgiye sahip olmamasıdır, bu nedenle, onu serbest bırakmak programcının görevidir ve derleyici bu işlemi etkili bir şekilde optimize edemez. Programcı bunu doğru şekilde yapmazsa, bir bellek sızıntısı olacaktır.

İşin iyi yanı, veriler yığında değil de yığında olduğundan işlev geri döndüğünde yok edilmeyecek olmasıdır. Böylece, veriler için işaretçiyi, artık yığın içinde bulunan bu verilere erişebilen diğer işlevlere kolayca aktarabilirsiniz.

Tüm bu konuşma esasen basit C için geçerlidir. Ancak, C ++ üzerindeki birçok veri yapısının temelinde aynı şey olur. Örneğin, C ++ std :: vektör, vektörün sonunda kaç öğe alabileceğini önceden bilmesinin bir yolu olmadığı için, vektörün her bir öğesini yığında değil, yığın üzerinde tahsis eder.

Açıklamamda muhtemelen birkaç kusur vardır. Okumak için prova yapmadım ama ana fikir bu.

tl; dr: Bir işleve yerel olan değişkenler yığın üzerinde tahsis edilir ve işlev döndüğünde otomatik olarak serbest bırakılır. Derleyicinin boyutu önceden tahmin edemediği (vektörler gibi) veya işaretçi olan (muhtemelen diğer işlevler tarafından kullanılacak olan) değişkenler öbek üzerinde tahsis edilir.


cevap 3:

Ana neden, yığının yerel CPU önbelleğine uymasıdır.

Bir int i ilan ettiğinizi düşünün; değişken; örneğin for (int i = 0; i

Sert bir Java kullanıcısı bile, i'yi "harici olarak" tahsis ederek (etkili bir şekilde, bir Int i = new Int (); çağrı yaparak) kaynakları boşa harcadığınızı anlayacaktır.

Şimdi, birkaç, hatta bir tam sayı büyüklüğünde bir sınıfınız olduğunu varsayalım. Kolay bir yol varsa, neden onu yeniyi kullanarak tahsis etmek istersiniz?

Bu kolay yol, C ++ 'da mümkündür ve yaygın olarak teşvik edilir, çünkü referans sayma tabanlı çöp toplama yerine C ++, kapsam tabanlı nesne yaşam süresi yönetimi kullanır. Derleyici, geçerli kapsam sona erdiğinde, içinde otomatik olarak tahsis edilen nesnelerin atılması gerektiğini bilir. Böylece, yerel olarak tahsis edilen nesneler için bellek düzeni, yığının iç içe geçmiş işlev çağrıları için nasıl çalıştığıyla uygun şekilde eşleşir.

Bu kolaylık elbette bir tesadüf değil. C ++ 'nın nesne yaşam süreleriyle nasıl ilgilendiğinin felsefesi budur.

Dezavantajı: Nesne yaşam süresi yönetiminin "esnekliğine" ihtiyaç duyulduğunda, düz otomatik olarak tahsis edilen nesneler çalışmaz - bunlar yığın üzerinde tahsis edildiklerinden, çağrılan işlev kapsamını terk ettikçe artık geçerli olmayacaklardır. Bu nedenle, programcı bu nesneleri daha büyük ölçekte açık bir şekilde "kalıcı" olarak beyan etmelidir - çoğu zaman akıllı işaretçiler kullanarak, ancak etkin bir şekilde bu, çıplak işaretçilerin ve ham yeni / silme işleminin gerekli olduğu alandır.

(Bu arada, C ++ 'nın yaygın bir tuzağı: Yığın tahsis edilmiş bir nesneye bir işaretçi / başvuruyu, kendisine tahsis edilen yığın çerçevesinin yeterince uzun süre yaşayacağından emin olmadan bir zaman uyumsuz çağrıya geçirmek. Bu durumda FTW gelecek ve vaat ediyor. )

Üst: Performans, mühendislik güzelliği ve nesne sahipliği yönetiminin dikkate değer öngörülebilirliği.

Bellek ayırmanın kendisine gelince, daha iyi önbelleğe alma için veri yerelliğine kıyasla performansta nispeten küçük bir kazançtır. Belleğin kendisinin yığın tahsisli nesneler için "tahsis edilmesine" gerek olmadığı doğrudur, ancak, yığın-tahsisli nesneler için kullanıcı tanımlı kurucular ve yıkıcılar, tabii ki çağrılır.

Aynı zamanda, çöp toplanan diller için böyle bir sıkıntı olan bellek parçalanması, yığın tabanlı tahsis ve yaşam süresi yönetimi için tamamen var olmayan bir sorundur.

Oh, ve bu kapsam tabanlı tahsisatın nihayet yapıyı kesinlikle gereksiz kıldığından bahsetmiş miydim? Öyle.


cevap 4:

Olabilir (Java'da genellikle bu konuda bir seçeneğiniz olmasa da)

İşte basit bir gerekçe: yığından ayırma yalnızca bir kaydı ayarlama meselesidir, öbekten ayırma ise bir işlev çağrısı içerir. İşlev çağrısı satır içi olsa bile, yığın veri yapıları genellikle önemsizdir (parçalanmayı önlemek için yeni nesnelerin yerleşimini optimize etme girişiminde).

Alıştırma: alloca ve malloc kullanarak bir C ++ programı derleyin ve ortaya çıkan koddaki farklılıklara bakın.

Şimdi, çöp toplanan bir sistemde, yığından ayırmak neredeyse aynı hızda olabilir, çünkü yığın bir

Bölge ayırıcı

. Ancak buna rağmen, genellikle bazı meta veri izleme nesneleri ve bazı senkronizasyonlar vardır, böylece birden çok iş parçacığının tümü yığından tahsis edebilir. Yığın üzerine tahsis, bunlardan hiçbirine ihtiyaç duymaz, çünkü fonksiyon sona erdiğinde her şeyi "patlatırız" ve her evre zaten kendi yığınına sahiptir.

Ek olarak, bellek konumu, yığın yerine yığından ayırma lehine çalışabilir. Bir iş parçacığı yığını için kullanılan bellek zaten CPU'nun L1 veya L2 önbelleğinde olabilir. Başka hiçbir iş parçacığının mevcut iş parçacığının yığınına erişmek için bir nedeni olmayacağından, başka bir CPU ile herhangi bir 'ping pong' davranışına sahip olma olasılığı da düşüktür.


cevap 5:

Yığın uygulamasına bağlıdır. Yığın yönetimi yoksa, o zaman bir fark yoktur, ancak sonunda yığın alanınız tükenir, onu daha çok kullanırsanız. Yığının bellekte düşük adreslerden yüksek adreslere gittiği ve yığının yüksekten düşüğe gittiği ve yığının yığına girene kadar kullanıldıkça büyümeye devam ettiği basit uygulamalar gördüm.

Yığın bellek, yığın bellekten ayrı tutulur, böylece birbirlerini dolaşma konusunda endişelenmeleri gerekmez.

Çoğu modern sistemde yığın yönetilir. Ayrılmamış belleği izler ve ayırma istendiğinde kullanır / yeniden kullanır. Daha yaygın uygulama, ayrılmamış yığını bağlantılı bir liste olarak ele alır. İstenen bellek boyutuna bağlı olarak, yığın, onu barındırabilecek boş alan arar ve onu tahsis edilmiş olarak işaretler. Yığını doğru boyutta olacak boş alan aramak doğrusal bir işlem olduğundan biraz zaman alır. Belleğin yığın üzerinde küçük parçalar halinde çok fazla ayrılması ve serbest bırakılması daha fazla zaman alır, çünkü bu, yığın içinde listede arama yapmak için daha fazla boş alan olmasına neden olur. Buna yığın parçalama denir. Yığın belleğini ayırmak / ayırmak için bunu en aza indirmesi gereken optimize edilmiş stratejiler vardır, ancak gerçek şu ki, yalnızca ilk yığın tahsisi yığın üzerindeki bir işlem kadar hızlı olacaktır.

Yığın, belleği sabit zamanda ayırır. Yığın alanını ayırmak ve ayırmak basittir. Yığını itmek için, yığın işaretçisini yığın çerçevesinin ötesine taşır ve yeni bir tane oluşturur. Ayrımı kaldırmak için, yığın işaretçisini önceki yığın çerçevesinin başlangıcına taşır. Yığın yaptığı gibi, ayrılmış bellekteki boşluklar hakkında endişelenmesine gerek yoktur.


cevap 6:
Yığın, öbekten daha hızlıdır, çünkü yığın belleğinin ayrıldığı sırada serbest bırakılması garanti edilir. Bu, yönetimi çok daha kolay hale getirir (örneğin, boş alanları birleştirmeye gerek yoktur) ve bellek erişimlerinin yerini optimize eder.

cevap 7:

Yığının tahsisi çok daha hızlıdır, çünkü tek yaptığı yığın işaretçisini hareket ettirmektir. Bellek havuzlarını kullanarak, yığın ayırmadan karşılaştırılabilir performans elde edebilirsiniz, ancak bu biraz ek karmaşıklık ve kendi baş ağrılarıyla birlikte gelir. Ayrıca, yığın ve yığın yalnızca bir performans değerlendirmesi değildir; aynı zamanda size nesnelerin beklenen ömrü hakkında çok şey söyler. Yığın tahsisini aşırı kullanıp kullanmadığınızı çabucak anlayacağınız için yığının boyutu sonludur. Uzun süre çalışan sunucu uygulamaları için yığın ayırma tercih edilir. En iyi yönetilen yığınlar bile sonunda o kadar parçalanır ki uygulama performansı düşer.


cevap 8:

Cevapların çoğu soyutlamalara fazlasıyla kapılmış durumda.

Yığın tahsisi, yığın işaretçisindeki basit bir alt giriş ile CPU seviyesinde yapılır.

Öte yandan yığın tahsisi, işletim sistemi veya belleği "yöneten" başka bir soyutlama tarafından yönetilir.

Hangisi daha hızlı - tek bir CPU talimatı mı yoksa bir işletim sisteminin bellek yönetimi algoritmasını çağırarak yapılan işlemler kümesi mi? ;)

Yığın tahsisleri çalışma zamanında yapılır, bu nedenle bir derleyicinin yığında bir şeyin nereye tahsis edildiğini bilmesinin bir yolu yoktur.


cevap 9:

Yığın düzeni derleme zamanında, yığın çalışma zamanında ayarlanır. Dolayısıyla, temel ayırma yığında biraz daha hızlıdır, ancak bu süre genellikle yapıcıyı çalıştırmak gibi nesne oluşturmadaki diğer adımlar tarafından cüceleştirilir.