Yığın ve yığın ne ve nerede?


8102

Programlama dil kitapları yığın üzerinde değer türlerinin oluşturulduğunu ve bu iki şeyin ne olduğunu açıklamadan yığın üzerinde referans türlerinin oluşturulduğunu açıklar. Bunun net bir açıklamasını okumadım. Bir yığının ne olduğunu anlıyorum . Fakat,

  • Nerede ve ne (fiziksel olarak gerçek bir bilgisayarın hafızasında)?
  • İşletim sistemi veya dil çalışma zamanı tarafından ne ölçüde kontrol ediliyorlar?
  • Kapsamı nedir?
  • Her birinin boyutunu ne belirler?
  • Birini daha hızlı yapan nedir?

175
gerçekten iyi bir açıklama burada bulunabilir Bir yığın ve yığın arasındaki fark nedir?
Songo

12
Ayrıca (gerçekten) iyi: codeproject.com/Articles/76153/… (yığın / yığın kısmı)
Ben


3
İlgili, bkz. Stack Clash . Stack Clash iyileştirmeleri, sistem değişkenlerinin ve gibi davranışların bazı yönlerini etkiledi rlimit_stack. Ayrıca bkz.Kırmızı
jww

3
@mattshane Yığın ve yığın tanımları hiçbir şekilde değer ve referans türlerine bağlı değildir. Başka bir deyişle, değer ve referans türleri hiç bulunmasa bile yığın ve yığın tam olarak tanımlanabilir. Ayrıca, değer ve referans türlerini anlarken, yığın sadece bir uygulama detayıdır. Eric Lippert: Yığın Bir Uygulama Detayıdır, Birinci Bölüm .
Matthew

Yanıtlar:


5964

Yığın, bir yürütme iş parçacığı için çizik alanı olarak ayrılan bellektir. Bir işlev çağrıldığında, yığının üstünde yerel değişkenler ve bazı defter tutma verileri için bir blok ayrılır. Bu işlev döndüğünde, blok kullanılmaz ve bir sonraki işlev çağrıldığında kullanılabilir. Yığın her zaman bir LIFO (son giren ilk çıkar) düzeninde ayrılır; en son ayrılmış blok her zaman serbest bırakılacak bir sonraki bloktur. Bu, yığını takip etmeyi gerçekten kolaylaştırır; bir bloğu yığından boşaltmak, bir işaretçiyi ayarlamaktan başka bir şey değildir.

Yığın, dinamik ayırma için bir kenara ayrılmıştır. Yığın aksine, blokların öbekten tahsis edilmesi ve dağıtılması için zorunlu bir model yoktur; istediğiniz zaman bir blok ayırabilir ve istediğiniz zaman serbest bırakabilirsiniz. Bu, herhangi bir zamanda yığının hangi bölümlerinin tahsis edildiğini veya serbest bırakıldığını takip etmeyi çok daha karmaşık hale getirir; farklı kullanım modelleri için yığın performansını ayarlamak için birçok özel yığın ayırıcı vardır.

Her iş parçacığı bir yığın alır, ancak uygulama için yalnızca bir yığın vardır (farklı ayırma türleri için birden çok yığın olması nadir olmamasına rağmen).

Sorularınızı doğrudan cevaplamak için:

İşletim sistemi veya dil çalışma zamanı tarafından ne ölçüde kontrol ediliyorlar?

İş parçacığı oluşturulduğunda işletim sistemi yığını, sistem düzeyindeki her iş parçacığı için ayırır. Genellikle işletim sistemi, yığının uygulamaya tahsis edilmesi için dil çalışma zamanı tarafından çağrılır.

Kapsamı nedir?

Yığın bir iş parçacığına eklenir, böylece iş parçasından çıkıldığında yığın geri kazanılır. Öbek genellikle uygulama başlangıcında çalışma zamanı tarafından ayrılır ve uygulama (teknik işlemden) çıktığında geri alınır.

Her birinin boyutunu ne belirler?

Bir iş parçacığı oluşturulduğunda yığının boyutu ayarlanır. Yığın boyutu uygulama başlangıcında ayarlanır, ancak alan gerektiğinde büyüyebilir (ayırıcı işletim sisteminden daha fazla bellek ister).

Birini daha hızlı yapan nedir?

Yığın daha hızlıdır, çünkü erişim düzeni bellek ayırmayı ve ondan ayırmayı önemsiz kılar (bir işaretçi / tam sayı basitçe artırılır veya azaltılır), öte yandan yığın bir ayırma veya ayırmaya dahil olan çok daha karmaşık defter tutma içerir. Ayrıca, yığındaki her bayt çok sık yeniden kullanma eğilimindedir, bu da işlemcinin önbelleğine eşleme eğilimi göstererek çok hızlı hale getirir. Öbek için bir başka performans, çoğunlukla küresel bir kaynak olan yığının tipik olarak çok iş parçacıklı güvenli olması gerektiğidir, yani her bir ayırma ve ayırmanın - genellikle - programdaki diğer tüm yığın erişimleriyle senkronize edilmesi gerekir.

Net bir gösteri:
Görüntü kaynağı: vikashazrati.wordpress.com


74
İyi cevap - ama bence süreç yığını (işletim sisteminin var olduğu varsayılarak) işletim sistemi tarafından tahsis edilirse, program tarafından satır içi olarak tutulur. Bu, yığının daha hızlı olmasının bir başka nedenidir - itme ve pop işlemleri tipik olarak bir makine talimatıdır ve modern makineler bunlardan en az 3 tanesini tek bir döngüde yapabilirken, öbek tahsisi veya serbest bırakılması OS kodunu çağırmayı içerir.
13'te

276
Sonunda diyagramla gerçekten kafam karıştı. Bu görüntüyü görene kadar anladığımı düşündüm.
Sina Madani

10
@Anarelle işlemci bir os ile veya os olmadan talimatları çalıştırır. Kalbime yakın bir örnek, bugün bildiğimiz gibi API çağrıları, işletim sistemi olmayan SNES'tir - ancak bir yığını vardı. Bir yığına ayırmak, bu sistemlere toplama ve çıkarma işlemidir ve bu, onları oluşturan işlevden geri dönerek patlatıldığında yok edilen değişkenler için iyidir, ancak sonucun sadece yapılamayacağı bir kurucu ile çelişir atılır. Bunun için çağrı ve geri dönüşe bağlı olmayan öbeğe ihtiyacımız var. Çoğu işletim sistemi API'ları bir yığın vardır, kendi başınıza yapmak için bir neden yoktur
sqykly

2
msgstr "yığın, çizik alanı olarak ayrılan bellektir". Güzel. Ama Java bellek yapısı açısından aslında "bir kenara" nerede? Yığın bellek / Yığın olmayan bellek / Diğer ( betsol.com/2017/06/… uyarınca Java bellek yapısı )
Jatin Shashoo

4
@JatinShashoo Java çalışma zamanı, bayt kodu yorumlayıcısı olarak bir sanallaştırma düzeyi daha ekler, bu nedenle bahsettiğiniz şey yalnızca Java uygulaması bakış açısıdır. İşletim sistemi açısından, Java çalışma zamanı işleminin alanının bir kısmını işlenmiş bayt kodu için "yığın olmayan" bellek olarak ayırdığı bir yığın olan her şey. Bu OS düzeyi yığınının geri kalanı, nesne verilerinin depolandığı uygulama düzeyinde yığın olarak kullanılır.
kbec

2349

Stack:

  • Yığın gibi bilgisayar RAM'inde saklanır.
  • Yığında oluşturulan değişkenler kapsam dışına çıkar ve otomatik olarak yeniden konumlandırılır.
  • Öbek üzerindeki değişkenlere göre tahsis edilmesi çok daha hızlıdır.
  • Gerçek bir yığın veri yapısı ile uygulanır.
  • Parametre geçişi için kullanılan yerel verileri, dönüş adreslerini saklar.
  • Yığın çok fazla kullanıldığında yığın taşması olabilir (çoğunlukla sonsuz veya çok derin özyineleme, çok büyük ayırmalar).
  • Yığında oluşturulan veriler işaretçiler olmadan kullanılabilir.
  • Derleme süresinden önce ne kadar veri ayırmanız gerektiğini biliyorsanız ve çok büyük olmadığında yığını kullanırsınız.
  • Genellikle, programınız başladığında önceden belirlenmiş bir maksimum boyuta sahiptir.

Yığın:

  • Yığın gibi bilgisayar RAM'inde saklanır.
  • C ++ 'da, öbek üzerindeki değişkenler el ile yok edilmeli ve asla kapsam dışı kalmamalıdır. Veri ile serbest delete, delete[]ya da free.
  • Yığındaki değişkenlere göre ayırmak daha yavaştır.
  • İstek üzerine, program tarafından kullanılmak üzere bir veri bloğu tahsis etmek için kullanılır.
  • Çok fazla tahsis ve anlaşma olduğunda parçalanma olabilir.
  • C ++ veya C'de, yığın üzerinde oluşturulan veriler işaretçilerle işaretlenecek ve sırasıyla newveya ile ayrılacaktır malloc.
  • Çok büyük bir arabellek atanması istendiğinde ayırma hataları olabilir.
  • Çalışma zamanında ne kadar veriye ihtiyacınız olacağını tam olarak bilmiyorsanız veya çok fazla veri ayırmanız gerekiyorsa yığını kullanırsınız.
  • Bellek sızıntılarından sorumludur.

Misal:

int foo()
{
  char *pBuffer; //<--nothing allocated yet (excluding the pointer itself, which is allocated here on the stack).
  bool b = true; // Allocated on the stack.
  if(b)
  {
    //Create 500 bytes on the stack
    char buffer[500];

    //Create 500 bytes on the heap
    pBuffer = new char[500];

   }//<-- buffer is deallocated here, pBuffer is not
}//<--- oops there's a memory leak, I should have called delete[] pBuffer;

31
İşaretçi pBuffer ve b değeri yığın üzerinde bulunur ve büyük olasılıkla işlevin girişinde tahsis edilir. Derleyiciye bağlı olarak, tampon fonksiyon girişinde de tahsis edilebilir.
Andy

36
Dil standardında Ctanımlandığı gibi C99( open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf adresinde mevcut olan) dilin bir "yığın" gerektirdiği yaygın bir yanılgıdır . Aslında, 'yığın' kelimesi standartta bile görünmez. Bu yanıtlar Cyığın kullanımı wrt / to genel olarak doğrudur, ancak hiçbir şekilde dil gerektirmez. Daha fazla bilgi için knosof.co.uk/cbook/cbook.html adresine bakın ve özellikle en.wikipedia.org/wiki/Burroughs_large_systemsC
johne

55
@Brian Yığın üzerinde neden buffer [] ve pBuffer işaretçisinin oluşturulduğunu ve yığın üzerinde pBuffer verilerinin neden oluşturulduğunu açıklamalısınız. Bence bazı ppl cevap tarafından karışık olabilir düşünüyorum çünkü program özellikle bellek yığını yığın vs tahsis tahsis talimat ama bu böyle değil. Tampon bir değer türü, pBuffer ise bir referans türü olduğu için mi?
Howiecamp

9
@Remover: Hiçbir işaretçi bir adres tutmaz ve yığın veya yığın üzerindeki bir şeye eşit olarak işaret edebilir. yeni, malloc ve malloc'a benzer diğer bazı fonksiyonlar öbek üzerinde tahsis edilir ve tahsis edilen belleğin adresini döndürür. Neden öbek üzerinde tahsis etmek istersiniz? Böylece hafızanız kapsam dışına çıkmayacak ve siz onu istediğiniz kadar serbest bırakılamayacak.
Brian R. Bondy

35
"Bellek sızıntılarından sorumlu" - Yığınlar bellek sızıntılarından sorumlu değildir! Tembel / Unutkan / ex-java kodlayıcılar / bok vermeyen kodlayıcılar!
Laz

1370

En önemli nokta, yığın ve yığının belleğin tahsis edilme yolları için genel terimler olmasıdır. Bunlar birçok farklı şekilde uygulanabilir ve terimler temel kavramlar için geçerlidir.

  • Bir öğe yığınında, öğeler oraya yerleştirildikleri sırayla üst üste otururlar ve yalnızca üstteki öğeyi kaldırabilirsiniz (her şeyi devirmeden).

    Kağıt yığını gibi istifleyin

    Bir yığının basitliği, ayrılan belleğin her bir bölümünün kaydını içeren bir tablo tutmanıza gerek olmamasıdır; ihtiyacınız olan tek durum bilgisi yığının sonuna doğru tek bir işaretçi. Tahsis etmek ve tahsisini kaldırmak için sadece o işaretçiyi arttırır ve azaltırsınız. Not: bazen bir bellek bölümünün en üstünde başlayıp yukarı doğru büyümek yerine aşağı doğru uzanmak için bir yığın uygulanabilir.

  • Bir yığın halinde, öğelerin yerleştirilme biçimine ilişkin özel bir emir yoktur. Net bir 'üst' öğe olmadığı için öğelere herhangi bir sırayla erişebilir ve bunları kaldırabilirsiniz.

    Bir yığın meyan kökü yığını gibi

    Yığın ayırma, hangi belleğin ayrıldığının ve neyin ayrılmadığının tam bir kaydını tutmayı ve parçalanmayı azaltmak için bazı genel bakımın yanı sıra, istenen boyuta uyacak kadar büyük bitişik bellek segmentleri bulmasını vb. Bellek, boş alan bırakarak herhangi bir zamanda yeniden yerleştirilebilir. Bazen bir bellek ayırıcı, ayrılan belleği hareket ettirerek belleği birleştirmek veya çöp toplama - bellek artık kapsamda olmadığında tanımlama ve yeniden yerleştirme gibi çalışma görevlerini gerçekleştirir.

Bu görüntüler, bir yığın ve yığın halinde bellek ayırmanın ve serbest bırakmanın iki yolunu tanımlamak için oldukça iyi bir iş çıkarmalıdır. Yum!

  • İşletim sistemi veya dil çalışma zamanı tarafından ne ölçüde kontrol ediliyorlar?

    Belirtildiği gibi, yığın ve yığın genel terimlerdir ve birçok şekilde uygulanabilir. Bilgisayar programları tipik olarak, çağrıldığı herhangi bir işleve bir işaretçi ve yerel değişkenler gibi geçerli işlevle ilgili bilgileri depolayan çağrı yığını adı verilen bir yığına sahiptir. İşlevler diğer işlevleri çağırıp geri döndüklerinden, yığın büyür ve işlevlerden bilgileri çağrı yığınının aşağısında tutmak için küçülür. Bir programın üzerinde gerçekten çalışma zamanı kontrolü yoktur; programlama dili, işletim sistemi ve hatta sistem mimarisi tarafından belirlenir.

    Yığın, dinamik ve rasgele atanan herhangi bir bellek için kullanılan genel bir terimdir; yani arızalı. Bellek genellikle işletim sistemi tarafından ayrılır ve uygulama bu ayırmayı yapmak için API işlevlerini çağıran bir işlevdir. Dinamik olarak ayrılan belleği yönetmek için genellikle kullanılan programlama dilinin veya ortamın çalışma zamanı kodu tarafından işlenen oldukça fazla ek yük vardır.

  • Kapsamı nedir?

    Çağrı yığını o kadar düşük seviyeli bir kavram ki programlama anlamında 'kapsam' ile ilgili değil. Bazı kodları parçalarına ayırırsanız, yığının bölümlerine ilişkin işaretçi stil referanslarını görürsünüz, ancak daha üst düzey bir dil söz konusu olduğunda, dil kendi kapsam kurallarını uygular. Bununla birlikte, bir yığının önemli bir yönü, bir işlev geri döndüğünde, o işleve yerel olan herhangi bir şeyin derhal yığından serbest bırakılmasıdır. Bu, programlama dillerinizin nasıl çalıştığı göz önüne alındığında, çalışmasını beklediğiniz şekilde çalışır. Bir yığın halinde tanımlamak da zordur. Kapsam, işletim sisteminin maruz kaldığı her şeydir, ancak programlama diliniz muhtemelen uygulamanızda "kapsam" ın ne olduğuna ilişkin kurallarını ekler. İşlemci mimarisi ve işletim sistemi sanal adresleme kullanıyor, işlemcinin fiziksel adreslere çevirdiği ve sayfa hataları vb. olduğu durumlarda, hangi sayfaların hangi uygulamalara ait olduğunu takip ederler. Bununla birlikte, bu konuda asla endişelenmenize gerek yoktur, çünkü programlama dilinizin belleği ayırmak ve boşaltmak için kullandığı yöntemi kullanırsınız ve hataları kontrol eder (ayırma / serbest bırakma herhangi bir nedenle başarısız olursa).

  • Her birinin boyutunu ne belirler?

    Yine, dile, derleyiciye, işletim sistemine ve mimariye bağlıdır. Bir yığın genellikle önceden ayrılır, çünkü tanım gereği bitişik bellek olmalıdır. Dil derleyici veya işletim sistemi boyutunu belirler. Yığın üzerinde büyük miktarda veri depolamazsınız, bu nedenle istenmeyen sonsuz özyineleme (dolayısıyla "yığın taşması") veya diğer olağandışı programlama kararları haricinde, asla tam olarak kullanılmaması için yeterince büyük olacaktır.

    Bir yığın, dinamik olarak ayrılabilen her şey için genel bir terimdir. Hangi yöne baktığınıza bağlı olarak, sürekli olarak boyut değiştiriyor. Modern işlemcilerde ve işletim sistemlerinde tam olarak nasıl çalıştığı zaten soyutlanmış durumdadır, bu yüzden normalde nasıl derinlemesine çalıştığı konusunda çok fazla endişelenmenize gerek yoktur, bunun dışında (size izin verdiği dillerde) bellek kullanmamanız gerekir. henüz tahsis etmediniz veya serbest bıraktığınız bellek.

  • Birini daha hızlı yapan nedir?

    Yığın daha hızlıdır çünkü tüm boş bellek her zaman bitişiktir. Tüm boş bellek segmentleri için bir liste bulundurulmasına gerek yoktur, sadece yığının geçerli üst kısmına tek bir işaretçi yerleştirilir. Derleyiciler genellikle bu işaretçiyi bu amaçla özel, hızlı bir kayıt defterinde saklar . Dahası, bir yığın üzerindeki sonraki işlemler genellikle çok yakın bir düzeyde bellek üzerinde yoğunlaşır ve bu çok düşük bir seviyede işlemci kalıp önbellekleri tarafından optimizasyon için iyidir.


20
David Bunun iyi bir imaj olduğunu ya da "aşağı itilen yığın" kavramını göstermek için iyi bir terim olduğunu kabul etmiyorum. Bir yığın şey eklediğinizde, yığının diğer içerikleri değildir nerede olduklarını kalır, aşağı itti.
thomasrutter

8
Bu cevap büyük bir hata içeriyor. Statik değişkenler yığına ayrılmaz. Açıklama için cevabım [link] stackoverflow.com/a/13326916/1763801 adresine bakın. "otomatik" değişkenleri "statik" değişkenlerle
denkleştiriyorsunuz

13
Özellikle, yığın üzerinde "statik olarak ayrılmış yerel değişkenler" ayrıldığını söylersiniz. Aslında veri segmentinde tahsis edilirler. Yığına yalnızca otomatik olarak atanan değişkenler (tüm yerel değişkenlerin çoğunu içermesine rağmen, hepsini içermez ve işlev parametreleri gibi değere göre iletilen değerler gibi) ayrılır.
davec

9
Az önce haklı olduğunuzu fark ettim - C'de statik ayırma , dinamik olmayan bir şey için bir terim yerine kendi ayrı bir şeydir . Cevabımı düzenledim, teşekkürler.
thomasrutter

5
Sadece C. Java, Pascal, Python ve diğerlerinin hepsi statik ve otomatik tahsisi ve dinamik tahsisi kavramlarına sahiptir. "Statik ayırma" demek hemen hemen her yerde aynı şey demektir. Hiçbir dilde statik ayırma "dinamik değil" anlamına gelmez. Tanımladığınız şey için "otomatik" ayırma terimini istiyorsunuz (örn. Yığıntaki şeyler).
davec

727

(Bu cevabı aşağı yukarı bunun bir kopyası olan başka bir sorudan taşıdım.)

Sorunuzun cevabı uygulamaya özeldir ve derleyiciler ve işlemci mimarileri arasında değişiklik gösterebilir. Ancak, burada basitleştirilmiş bir açıklama var.

  • Yığın ve yığın, temeldeki işletim sisteminden ayrılan bellek alanlarıdır (genellikle talep üzerine fiziksel belleğe eşlenen sanal bellek).
  • Çok iş parçacıklı bir ortamda, her iş parçacığının kendi bağımsız yığını vardır, ancak yığını paylaşırlar. Eşzamanlı erişim yığın üzerinde kontrol edilmelidir ve yığın üzerinde mümkün değildir.

Öbek

  • Yığın, kullanılmış ve serbest blokların bağlantılı bir listesini içerir. Yığındaki ( newveya malloc) yeni tahsisler , serbest bloklardan birinden uygun bir blok oluşturularak yerine getirilir. Bu, öbek üzerindeki blok listesinin güncellenmesini gerektirir. Öbek üzerindeki bloklarla ilgili bu meta bilgi , öbek üzerinde genellikle her bloğun hemen önünde küçük bir alanda depolanır.
  • Yığın büyüdükçe yeni bloklar genellikle alt adreslerden yüksek adreslere ayrılır. Böylece öbeği , bellek tahsis edildikçe büyüyen bir bellek bloğu yığını olarak düşünebilirsiniz . Yığın bir ayırma için çok küçükse, boyut genellikle altta yatan işletim sisteminden daha fazla bellek edinilerek artırılabilir.
  • Birçok küçük bloğu ayırmak ve yeniden yerleştirmek, yığını kullanılan bloklar arasına serpiştirilmiş çok sayıda küçük serbest bloğun olduğu bir durumda bırakabilir. Büyük bir blok tahsis etme talebi başarısız olabilir, çünkü serbest blokların hiçbiri, serbest blokların birleştirilmiş boyutu yeterince büyük olmasına rağmen tahsis talebini karşılayacak kadar büyük değildir. Buna yığın parçalanması denir .
  • Serbest bir bloğa bitişik olan kullanılmış bir blok yeniden yerleştirildiğinde, yığının parçalanmasını etkili bir şekilde azaltan daha büyük bir serbest blok oluşturmak için yeni serbest blok, bitişik serbest blok ile birleştirilebilir.

Öbek

Yığın

  • Yığın genellikle CPU üzerinde yığın işaretçisi adı verilen özel bir kayıtla birlikte çalışır . Başlangıçta yığın işaretçisi yığının üstünü işaret eder (yığıntaki en yüksek adres).
  • CPU'nun itmek için özel talimatları var yığına değerleri ve haşhaş yığına geri onları. Her itme , değeri yığın işaretçisinin geçerli konumuna depolar ve yığın işaretçisini azaltır. Bir pop , yığın işaretçisinin işaret ettiği değeri alır ve sonra yığın işaretçisini artırır (yığına bir değer eklemenin yığın işaretçisini azaltması ve bir değer kaldırmanın artması gerçeğiyle karıştırmayın onu unutmayın. alt). Saklanan ve alınan değerler CPU kayıtlarının değeridir.
  • Bir işlev çağrıldığında, CPU geçerli talimat işaretçisini iten özel talimatlar kullanır , yani yığın üzerinde yürütülen kodun adresi. CPU daha sonra yönerge işaretçisini çağrılan işlevin adresine ayarlayarak işleve atlar. Daha sonra, işlev geri döndüğünde, eski komut işaretçisi yığından çıkarılır ve işleve çağrıdan hemen sonra yürütme kodda devam eder.
  • Bir işlev girildiğinde, yığın işaretçisi yerel (otomatik) değişkenler için yığın üzerinde daha fazla alan ayırmak üzere azaltılır. İşlev bir yerel 32 bit değişkene sahipse, yığın üzerinde dört bayt bir kenara bırakılır. İşlev döndüğünde, yığın işaretçisi ayrılan alanı boşaltmak için geri hareket ettirilir.
  • Bir fonksiyonun parametreleri varsa, bunlar fonksiyon çağrılmadan önce yığına itilir. İşlevdeki kod, daha sonra bu değerleri bulmak için yığının geçerli yığın işaretçisinden yukarı çıkmasını sağlar.
  • Yuvalama işlevi çağrıları bir cazibe gibi çalışır. Her yeni çağrı fonksiyon parametreleri, yerel değişkenler için dönüş adresi ve alan tahsis eder ve bu aktivasyon kayıtları iç içe çağrılar için istiflenebilir ve fonksiyonlar döndüğünde doğru şekilde çözülür.
  • Yığın sınırlı bir bellek bloğu olduğundan, çok fazla iç içe işlev çağırarak ve / veya yerel değişkenler için çok fazla alan ayırarak yığının taşmasına neden olabilirsiniz . Genellikle yığın için kullanılan bellek alanı, yığının alt kısmına (en düşük adres) yazmak CPU'da bir tuzak veya istisna tetikleyecek şekilde ayarlanır. Bu istisnai durum daha sonra çalışma zamanı tarafından yakalanabilir ve bir tür yığın taşması istisnasına dönüştürülebilir.

Yığın

Yığın üzerinde yığın yerine bir işlev tahsis edilebilir mi?

Hayır, yalnızca bu değişkenleri depolamak için değil, aynı zamanda iç içe geçmiş işlev çağrılarını takip etmek için kullanılan yığınlara işlevler için etkinleştirme kayıtları (yerel veya otomatik değişkenler) ayrılır.

Yığın yönetimi gerçekten çalışma zamanı ortamına bağlıdır. C kullanır mallocve C ++ kullanır new, ancak diğer birçok dilde çöp toplama vardır.

Bununla birlikte, yığın işlemci mimarisine yakından bağlı daha düşük seviyeli bir özelliktir. Yeterli alan olmadığında yığının büyütülmesi, yığını işleyen kütüphane çağrısında uygulanabileceğinden çok zor değildir. Bununla birlikte, istif taşması sadece çok geç olduğunda keşfedildiği için istifin büyütülmesi genellikle imkansızdır; ve uygulanabilir tek seçenek yürütme iş parçacığını kapatmak.


35
@Martin - Daha soyut kabul edilen cevaptan çok iyi bir cevap / açıklama. Bir vis fonksiyon çağrısında kullanılan yığın işaretleyicilerini / kayıtlarını gösteren örnek bir montaj programı daha açıklayıcı olacaktır.
Bikal Lem

3
Her referans türü, değer türlerinin bileşimidir (int, dize vb.). Söylendiği gibi, bu değer türleri, referans türünün bir parçası olduklarında nasıl çalıştığından daha fazla yığın halinde saklanır.
Nps

15
Bu cevap bence en iyisiydi, çünkü bir dönüş ifadesinin gerçekte ne olduğunu ve arada bir karşılaştığım "dönüş adresi" ile nasıl bir ilişki kurduğunu anlamama yardımcı oldu, ve fonksiyonların neden yığınlara itildiği. Mükemmel cevap!
Alex

3
Bu benim görüşüme göre en iyisi, yani yığın / yığın çok uygulamaya özgü olduğunu belirtmek için. Diğer cevaplar , dil ve çevre / işletim sistemi hakkında birçok şey varsayar . +1
Qix - MONICA KARŞILAŞTIRILDI

2
Ne demek istersiniz "İşlevdeki kod, bu değerleri bulmak için yığını geçerli yığın işaretçisinden yukarı doğru hareket ettirebilir." ? Bu konuyu biraz açıklayabilir misiniz lütfen?
Koray Tugay

404

Aşağıdaki C # kodunda

public void Method1()
{
    int i = 4;
    int y = 2;
    class1 cls1 = new class1();
}

Bellek şu şekilde yönetilir

Yığındaki değişkenlerin resmi

Local Variablesyalnızca işlev çağırma yığına girdiği sürece sürmelidir. Öbek, yaşamı önceden bilmediğimiz değişkenler için kullanılır, ancak bir süre dayanmasını bekleriz. Çoğu dilde derleme sırasında bir değişkeni yığına depolamak istiyorsak ne kadar büyük olduğunu bilmemiz önemlidir.

Nesneler (bunları güncelledikçe boyut olarak değişir) öbek üzerinde gider, çünkü oluşturma zamanında ne kadar süreceklerini bilmiyoruz. Birçok dilde yığın, artık referansı olmayan nesneleri (cls1 nesnesi gibi) bulmak için toplanan çöptür.

Java'da, çoğu nesne doğrudan yığın içine gider. C / C ++ gibi dillerde, işaretçilerle uğraşmadığınızda yapılar ve sınıflar yığın üzerinde kalabilir.

Daha fazla bilgiyi burada bulabilirsiniz:

Yığın ve yığın bellek ayırma arasındaki fark «timmurphy.org

ve burada:

Yığın ve Öbek Üzerinde Nesne Oluşturma

Bu makale yukarıdaki resmin kaynağıdır: Altı önemli .NET kavramı: Yığın, yığın, değer türleri, referans türleri, boks ve kutudan çıkarma - CodeProject

ancak bazı yanlışlıklar içerebileceğini unutmayın.


15
Bu yanlış. i ve cl'ler "statik" değişkenler değildir. bunlara "yerel" veya "otomatik" değişkenler denir. Bu çok önemli bir ayrım. Açıklama için [link] stackoverflow.com/a/13326916/1763801
adresini ziyaret edin

9
Statik değişken olduklarını söylemedim . Ben int ve cls1 statik öğeler olduğunu söyledi . Bellekleri statik olarak ayrılmıştır ve bu nedenle yığına giderler. Bu, dinamik bellek tahsisi gerektiren bir nesnenin aksine, öbür tarafta devam eder.
Snowcrash

12
"Statik eşyalar ... yığının üstüne çık" diyorum. Bu sadece yanlış. Statik öğeler veri segmentine, otomatik öğeler yığına gider.
davec

14
Ayrıca kim bu kod projesini yazmışsa onun neden bahsettiğini bilmiyor. Örneğin, "ilkel olanlar tamamen yanlış olan statik tip belleğe ihtiyaç duyar" diyor. Hiçbir şey yığın ilkel dinamik olarak tahsis etmekten alıkoyan, sadece "int dizi [] = yeni int [num]" ve voila gibi bir şey yazmak, .NET. Bu birkaç yanlışlıktan sadece biri.
davec

8
Yığınızda ve yığınta neler olduğu konusunda ciddi teknik hatalar yaptığınız için yazınızı düzenledim.
Tom Leys

209

Yığın Bir işlevi çağırdığınızda, o işleve ilişkin argümanlar artı yığına başka bir ek yük konur. Bazı bilgiler (dönüşte nereye gideceğiniz gibi) da burada saklanır. İşlevinizin içinde bir değişken bildirdiğinizde, bu değişken yığına da ayrılır.

Yığının yeniden konumlandırılması oldukça basittir, çünkü her zaman ayırdığınız ters sırada yeniden konumlandırırsınız. Yığın şeyler, işlevleri girerken eklenir, bunlardan çıktıkça ilgili veriler kaldırılır. Bu, çok sayıda başka işlevi çağıran (veya özyinelemeli bir çözüm oluşturmadan) çok sayıda işlev çağırmadıkça yığının küçük bir bölgesinde kalma eğiliminiz olduğu anlamına gelir.

Yığın Yığın, oluşturduğunuz verileri anında koyduğunuz genel bir addır. Programınızın kaç tane uzay gemisi oluşturacağını bilmiyorsanız, her bir uzay gemisini oluşturmak için yeni (veya malloc veya eşdeğeri) operatörü kullanmanız olasıdır. Bu tahsis bir süre devam edecek, bu yüzden işleri yarattığımızdan farklı bir sırayla serbest bırakacağız.

Bu nedenle, yığın çok daha karmaşıktır, çünkü sonunda yığınlarla kullanılmayan bellek bölgeleri olur - bellek parçalanır. İhtiyacınız olan boyutta boş hafıza bulmak zor bir sorundur. Bu yüzden öbekten kaçınılmalıdır (yine de sıklıkla kullanılmaktadır).

Uygulama Yığın ve yığının uygulanması genellikle çalışma zamanına / işletim sistemine bağlıdır. Genellikle performans açısından kritik olan oyunlar ve diğer uygulamalar, yığından büyük miktarda bellek alan ve bellek için işletim sistemine güvenmekten kaçınmak için dahili olarak çırparak kendi bellek çözümlerini oluşturur.

Bu, yalnızca bellek kullanımınızın normdan oldukça farklı olması durumunda pratiktir - yani büyük bir işlemde bir seviye yüklediğiniz ve başka bir büyük işlemde her şeyi parçalayabileceğiniz oyunlar için.

Bellekteki fiziksel konum Bu, programınızın fiziksel verilerin başka bir yerde (sabit diskte bile!) Olduğu belirli bir adrese erişiminiz olduğunu düşünmesini sağlayan Sanal Bellek adlı bir teknoloji nedeniyle düşündüğünüzden daha az önemlidir . Yığın için aldığınız adresler, arama ağacınız derinleştikçe artan sıradadır. Öbek için adresler öngörülemez (yani alışmalara özgü) ve açıkçası önemli değil.


16
Yığını kullanmaktan kaçınmak için bir öneri oldukça güçlüdür. Modern sistemler iyi yığın yöneticilerine sahiptir ve modern dinamik diller öbeği kapsamlı bir şekilde kullanır (programcı gerçekten endişelenmeden). Yığını kullanın diyebilirim, ancak manuel bir ayırıcıyla, ücretsiz unutma!
Greg Hewgill

2
Yığını veya yığını kullanabiliyorsanız, yığını kullanın. Yığını kullanamıyorsanız, gerçekten başka seçenek yok. Her ikisini de çok kullanıyorum ve elbette std :: vector ya da benzeri bir yığın kullanarak yığın. Bir acemi için, yığın sadece çok kolay olduğu için yığın kaçının !!
Tom Leys

Diliniz çöp toplama uygulamıyorsa, Akıllı işaretçiler (dinamik olarak ayrılmış bellek parçaları için referans sayımı yapan bir işaretçiyi saran ayrı ayrı atanan nesneler) çöp toplama ile yakından ilişkilidir ve yığını güvenli bir şekilde yönetmenin iyi bir yoludur ve sızıntısız bir şekilde. Çeşitli çerçevelerde uygulanırlar, ancak kendi programlarınız için de uygulanması zor değildir.
BenPen

1
"Bu yüzden öbek kaçınılmalıdır (yine de sık sık kullanılmasına rağmen)." Bunun pratikte ne anlama geldiğinden emin değilim, özellikle de bellek birçok üst düzey dilde farklı şekilde yönetiliyor. Bu soru dile agnostik olarak etiketlendiğinden, bu özel yorumun / satırın yanlış yerleştirildiğini ve uygulanabilir olmadığını söyleyebilirim.
LintfordPickle

2
İyi bir nokta @JonnoHampson - Geçerli bir noktaya işaret ederken, bir GC ile "yüksek seviyede bir dilde" çalışıyorsanız, muhtemelen bellek ayırma mekanizmalarını umursamadığınızı iddia ediyorum - ve bu yüzden yığının ve yığının ne olduğuna bile dikkat edin.
Tom Leys

194

Açıklığa kavuşturmak için, bu cevap yanlış bilgiye sahip ( thomas yorumdan sonra cevabını düzeltti, havalı :)). Diğer cevaplar sadece statik ayırmanın ne anlama geldiğini açıklamaktan kaçınır. Bu yüzden üç temel tahsisat biçimini ve bunların genellikle aşağıdaki yığın, yığın ve veri segmentiyle nasıl ilişkili olduğunu açıklayacağım. Ayrıca insanların anlamalarına yardımcı olmak için hem C / C ++ hem de Python'da bazı örnekler göstereceğim.

"Statik" (AKA statik olarak ayrılmış) değişkenleri yığına atanmaz. Öyle varsaymayın - birçok insan sadece "statik" sesin "yığın" gibi olduğu için bunu yapar. Aslında ne yığında ne de yığınta bulunurlar. , Veri segmenti olarak adlandırılan şeyin bir parçasıdır .

Bununla birlikte, genellikle "yığın" ve "yığın" yerine " kapsam " ve " ömür boyu " düşünmek daha iyidir.

Kapsam, kodun hangi bölümlerinin bir değişkene erişebileceğini ifade eder. Genel olarak, kapsamı daha karmaşık hale getirebilmesine rağmen , yerel kapsamı (yalnızca geçerli işlev tarafından erişilebilir) küresel kapsamı (her yerden erişilebilir) düşünüyoruz.

Ömür, programın yürütülmesi sırasında bir değişkenin ne zaman tahsis edildiğini ve yeniden ayrıldığını ifade eder. Genellikle statik ayırmayı düşünüyoruz (değişken, programın tüm süresi boyunca devam eder ve aynı işlevi birkaç işlev çağrısında depolamak için yararlı kılar) ve otomatik ayırmaya (değişken yalnızca bir işleve yapılan tek bir çağrı sırasında devam eder ve dinamik ayırmaya (süresi çalışma zamanında statik veya otomatik gibi derleme zamanı yerine tanımlanmış olan değişkenler ) karşı yalnızca işleviniz sırasında kullanılan ve işiniz bittiğinde atılabilen bilgileri depolama .

Çoğu derleyici ve yorumlayıcı bu davranışı yığınlar, yığınlar vb. Açısından benzer şekilde uygulasa da, bir derleyici bazen davranış doğru olduğu sürece bu kuralları kırabilir. Örneğin, optimizasyon nedeniyle, yerel değişkenlerin çoğu, bir kayıtta var olabilir veya yığında çoğu yerel değişken bulunsa bile, tamamen kaldırılabilir. Birkaç yorumda belirtildiği gibi, bir yığın veya yığın bile kullanmayan bir derleyici uygulamakta özgürsünüz, ancak diğer bazı depolama mekanizmaları (nadiren yapılır, çünkü yığınlar ve yığınlar bunun için mükemmeldir).

Tüm bunları göstermek için bazı basit açıklamalı C kodu sağlayacaktır. Öğrenmenin en iyi yolu bir programı hata ayıklayıcı altında çalıştırmak ve davranışı izlemek. Python okumayı tercih ederseniz, cevabın sonuna atlayın :)

// Statically allocated in the data segment when the program/DLL is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in the code
int someGlobalVariable;

// Statically allocated in the data segment when the program is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in this particular code file
static int someStaticVariable;

// "someArgument" is allocated on the stack each time MyFunction is called
// "someArgument" is deallocated when MyFunction returns
// scope - can be accessed only within MyFunction()
void MyFunction(int someArgument) {

    // Statically allocated in the data segment when the program is first loaded
    // Deallocated when the program/DLL exits
    // scope - can be accessed only within MyFunction()
    static int someLocalStaticVariable;

    // Allocated on the stack each time MyFunction is called
    // Deallocated when MyFunction returns
    // scope - can be accessed only within MyFunction()
    int someLocalVariable;

    // A *pointer* is allocated on the stack each time MyFunction is called
    // This pointer is deallocated when MyFunction returns
    // scope - the pointer can be accessed only within MyFunction()
    int* someDynamicVariable;

    // This line causes space for an integer to be allocated in the heap
    // when this line is executed. Note this is not at the beginning of
    // the call to MyFunction(), like the automatic variables
    // scope - only code within MyFunction() can access this space
    // *through this particular variable*.
    // However, if you pass the address somewhere else, that code
    // can access it too
    someDynamicVariable = new int;


    // This line deallocates the space for the integer in the heap.
    // If we did not write it, the memory would be "leaked".
    // Note a fundamental difference between the stack and heap
    // the heap must be managed. The stack is managed for us.
    delete someDynamicVariable;

    // In other cases, instead of deallocating this heap space you
    // might store the address somewhere more permanent to use later.
    // Some languages even take care of deallocation for you... but
    // always it needs to be taken care of at runtime by some mechanism.

    // When the function returns, someArgument, someLocalVariable
    // and the pointer someDynamicVariable are deallocated.
    // The space pointed to by someDynamicVariable was already
    // deallocated prior to returning.
    return;
}

// Note that someGlobalVariable, someStaticVariable and
// someLocalStaticVariable continue to exist, and are not
// deallocated until the program exits.

Yaşam boyu ve kapsamı ayırt etmenin neden önemli olduğuna dair özellikle dokunaklı bir örnek, bir değişkenin yerel kapsamı ancak statik yaşam süresi olabileceğidir - örneğin, yukarıdaki kod örneğinde "someLocalStaticVariable". Bu değişkenler, ortak ancak gayri resmi adlandırma alışkanlıklarımızı çok kafa karıştırıcı yapabilir. Örneğin, " yerel " dediğimizde genellikle " yerel olarak kapsamlandırılmış otomatik olarak ayrılmış değişken " anlamına geliriz ve küresel dediğimizde genellikle " global olarak kapsamlandırılmış statik olarak ayrılmış değişken " anlamına gelir . Ne yazık ki " dosya statik olarak tahsis edilen değişkenler kapsamına geldiğinde" " birçok kişi sadece ... " ha ??? ".

C / C ++ 'daki sözdizimi seçeneklerinden bazıları bu sorunu daha da kötüleştirmektedir - örneğin, birçok kişi aşağıda gösterilen sözdizimi nedeniyle genel değişkenlerin "statik" olmadığını düşünmektedir.

int var1; // Has global scope and static allocation
static int var2; // Has file scope and static allocation

int main() {return 0;}

Yukarıdaki bildirime "statik" anahtar sözcüğünü koymanın, var2'nin genel kapsamına sahip olmasını engellediğini unutmayın. Bununla birlikte, global var1 statik tahsise sahiptir. Bu sezgisel değil! Bu nedenle, kapsamı açıklarken asla "statik" kelimesini kullanmaya ve bunun yerine "dosya" veya "dosya sınırlı" kapsamı gibi şeyler söylemeye çalışıyorum. Ancak, birçok kişi yalnızca bir kod dosyasından erişilebilen bir değişkeni tanımlamak için "statik" veya "statik kapsam" ifadesini kullanır. Ömür boyu bağlamında, "statik" her zaman değişkenin program başlangıcında tahsis edildiği ve program çıktığında yeniden konumlandırıldığı anlamına gelir.

Bazı insanlar bu kavramları C / C ++ 'ya özgü olarak düşünür. Onlar değil. Örneğin, aşağıdaki Python örneği üç tahsisat türünü de göstermektedir (buraya girmeyeceğim yorumlanmış dillerde bazı küçük farklılıklar olabilir).

from datetime import datetime

class Animal:
    _FavoriteFood = 'Undefined' # _FavoriteFood is statically allocated

    def PetAnimal(self):
        curTime = datetime.time(datetime.now()) # curTime is automatically allocatedion
        print("Thank you for petting me. But it's " + str(curTime) + ", you should feed me. My favorite food is " + self._FavoriteFood)

class Cat(Animal):
    _FavoriteFood = 'tuna' # Note since we override, Cat class has its own statically allocated _FavoriteFood variable, different from Animal's

class Dog(Animal):
    _FavoriteFood = 'steak' # Likewise, the Dog class gets its own static variable. Important to note - this one static variable is shared among all instances of Dog, hence it is not dynamic!


if __name__ == "__main__":
    whiskers = Cat() # Dynamically allocated
    fido = Dog() # Dynamically allocated
    rinTinTin = Dog() # Dynamically allocated

    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

    Dog._FavoriteFood = 'milkbones'
    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

# Output is:
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is milkbones
# Thank you for petting me. But it's 13:05:02.256000, you should feed me. My favorite food is milkbones

Bir işlev içinde yalnızca yerel erişilebilirliğe sahip olduğu bildirilen statik bir değişkeni ifade eder , ancak genellikle "kapsam" terimini kullanmaz. Ayrıca, dillerin esasen sıfır esnekliğe sahip olduğu bir yığın / yığın yönünün, yığın üzerinde yürütme bağlamını kaydeden bir dil, oluşturuldukları bağlamları aşması gereken şeyleri tutmak için aynı yığını kullanamaz. . Bazı dillerde PostScriptbirden çok yığın bulunur, ancak daha çok yığın gibi davranan bir "yığın" vardır.
supercat

@supercat Her şey mantıklı. Kapsamı "kodun hangi bölümlerinin bir değişkene erişebileceği " olarak tanımladım (ve bunun en standart tanım olduğunu hissediyorum), bu yüzden kabul ettiğimizi düşünüyorum :)
davec

Bir değişkenin "kapsamını" zaman ve mekan ile sınırlı olarak görürüm. Sınıf-nesne kapsamındaki bir değişken, nesne var olduğu sürece değerini korumak için gereklidir. Yürütme bağlamı kapsamındaki bir değişken, yürütme bu bağlamda kaldığı sürece değerini korumak için gereklidir. Statik değişken bildirimi , kapsamı geçerli bloğa bağlı olan ve kapsamı sınırlandırılmamış bir değişkene eklenen bir tanımlayıcı oluşturur .
supercat

@supercat Bu yüzden ömür boyu kelimesini kullanıyorum, bu yüzden zaman kapsamı olarak adlandırdığımı ifade ediyorum. "Kapsam" kelimesini çok fazla anlamla aşırı yükleme ihtiyacını azaltır. Anlayabildiğim kadarıyla, kanonik kaynaklar arasında bile kesin tanımlar üzerinde tam bir fikir birliği var gibi görünmüyor. Terminolojim kısmen K&R'den, kısmen de çalıştığım / öğrettiğim ilk CS departmanındaki geçerli kullanımdan alınmıştır. Bilgilendirilmiş bir görüş duymak her zaman iyidir.
davec

1
Dalga geçiyor olmalısın. gerçekten bir fonksiyon içindeki statik değişkeni tanımlayabilir misiniz?
Zaeem Sattar

168

Diğerleri geniş vuruşları oldukça iyi yanıtladı, bu yüzden birkaç ayrıntıya atıyorum.

  1. Yığın ve yığın tekil olmak zorunda değildir. Birden fazla yığının olduğu yaygın bir durum, bir işlemde birden fazla iş parçacığının bulunmasıdır. Bu durumda, her iş parçacığının kendi yığını vardır. Birden fazla yığına sahip olabilirsiniz, örneğin bazı DLL yapılandırmaları farklı yığınlardan farklı DLL'lerin ayrılmasına neden olabilir, bu nedenle farklı bir kütüphane tarafından ayrılan belleği serbest bırakmak genellikle kötü bir fikirdir.

  2. C'de yığın üzerinde tahsis edilen yığın yerine tahsis edilen alloca kullanarak değişken uzunluk tahsisi yararlanabilir . Bu bellek dönüş ifadenizden kurtulamaz, ancak bir çizik tamponu için yararlıdır.

  3. Windows'da çok fazla kullanmadığınız büyük bir geçici arabellek yapmak ücretsiz değildir. Bunun nedeni, derleyicinin yığının var olduğundan emin olmak için işlevinize her girildiğinde çağrılan bir yığın sonda döngüsü oluşturmasıdır (çünkü Windows, yığının ne zaman büyümesi gerektiğini algılamak için yığının sonunda tek bir koruma sayfası kullanır. Yığının sonunda birden fazla sayfaya belleğe erişirseniz kilitlenirsiniz). Misal:

void myfunction()
{
   char big[10000000];
   // Do something that only uses for first 1K of big 99% of the time.
}

Re "ayırmak yerine": "Malloc yerine" demek mi?
Peter Mortensen

Ne kadar taşınabilir alloca?
Peter Mortensen

@PeterMortensen POSIX değil, taşınabilirlik garanti edilmez.
Don Neufeld

135

Diğerleri sorunuzu doğrudan yanıtladı, ancak yığını ve yığını anlamaya çalışırken, geleneksel bir UNIX işleminin (iş parçacıkları ve mmap()tabanlı ayırıcılar olmadan) bellek düzenini düşünmenin yararlı olduğunu düşünüyorum . Bellek Yönetimi Sözlüğü web sayfası bu hafıza düzeninin bir sahiptir.

Yığın ve yığın geleneksel olarak işlemin sanal adres alanının ters uçlarında bulunur. Yığın, erişildiğinde çekirdek tarafından ayarlanan bir boyuta (otomatik olarak ayarlanabilir setrlimit(RLIMIT_STACK, ...)) kadar otomatik olarak büyür . Bellek ayırıcısı brk()veya sbrk()sistem çağrısını çağırdığında yığın daha fazla fiziksel bellek sayfasını işlemin sanal adres alanına eşleştirir.

Bazı gömülü sistemler gibi sanal belleğe sahip olmayan sistemlerde, yığın ve yığın boyutlarının sabitlenmesi dışında genellikle aynı temel düzen uygulanır. Bununla birlikte, diğer gömülü sistemlerde (Microchip PIC mikrodenetleyicilerine dayananlar gibi), program yığını veri taşıma talimatları ile adreslenemeyen ayrı bir bellek bloğudur ve yalnızca program akış talimatları (çağrı, dönüş, vb.). Intel Itanium işlemciler gibi diğer mimarilerde birden çok yığın bulunur . Bu anlamda, yığın CPU mimarisinin bir öğesidir.


117

Yığın nedir?

Bir yığın, tipik olarak düzgün bir şekilde düzenlenmiş bir nesne yığınıdır.

Resim açıklamasını buraya girin

Bilgi işlem mimarisindeki yığınlar, verilerin ilk giren ilk çıkar yöntemiyle eklendiği veya kaldırıldığı bellek bölgeleridir.
Çok iş parçacıklı bir uygulamada, her iş parçacığının kendi yığını olacaktır.

Öbek nedir?

Bir yığın gelişigüzel yığılmış şeylerin düzensiz bir koleksiyonudur.

Resim açıklamasını buraya girin

Bilgi işlem mimarilerinde yığın, işletim sistemi veya bellek yöneticisi kitaplığı tarafından otomatik olarak yönetilen dinamik olarak ayrılmış bellek alanıdır.
Öbek üzerindeki bellek, program yürütülürken düzenli olarak ayrılır, yeniden yerleştirilir ve yeniden boyutlandırılır ve bu, parçalanma adı verilen bir soruna yol açabilir.
Parçalanma, bellek nesneleri arasında ek bellek nesnelerini tutamayacak kadar küçük olan küçük boşluklar ile ayrıldığında meydana gelir.
Net sonuç, daha fazla bellek ayırma için kullanılamayan yığın alanının yüzdesidir.

İkisi birlikte

Çok iş parçacıklı bir uygulamada, her iş parçacığının kendi yığını olacaktır. Ancak, tüm farklı iş parçacıkları yığın paylaşacak.
Farklı iş parçacıkları yığını çok iş parçacıklı bir uygulamada paylaştıkları için, bu aynı zamanda iş parçacıkları arasındaki yığındaki aynı bellek parçalarına erişmeye ve işlemeye çalışmamaları için iş parçacıkları arasında bir miktar koordinasyon olması gerektiği anlamına gelir. Aynı zaman.

Hangisi daha hızlı - yığın veya yığın? Ve neden?

Yığın yığından çok daha hızlıdır.
Bunun nedeni hafızanın yığına tahsis edilmesidir.
Yığın üzerinde bellek ayırmak, yığın işaretçisini yukarı taşımak kadar basittir.

Programlamaya yeni başlayan insanlar için, yığını daha kolay kullanmak muhtemelen iyi bir fikirdir.
Yığın küçük olduğu için, verileriniz için tam olarak ne kadar belleğe ihtiyaç duyacağınızı veya verilerinizin boyutunun çok küçük olduğunu biliyorsanız kullanmak istersiniz.
Verileriniz için çok fazla belleğe ihtiyacınız olacağını bildiğinizde yığını kullanmak daha iyidir veya ne kadar belleğe ihtiyacınız olduğundan emin değilsiniz (dinamik bir dizide olduğu gibi).

Java Bellek Modeli

Resim açıklamasını buraya girin

Yığın, yerel değişkenlerin (yöntem parametreleri dahil) depolandığı bellek alanıdır. Nesne değişkenleri söz konusu olduğunda, bunlar yalnızca öbek üzerindeki gerçek nesnelere referanslardır (işaretçiler).
Bir nesne her başlatıldığında, o nesnenin verilerini (durumunu) tutmak için yığın bellek yığını ayrılır. Nesneler başka nesneler içerebileceğinden, bu verilerin bir kısmı aslında bu iç içe geçmiş nesnelere referanslar içerebilir.


115

Yığın, 'pop' (yığındaki bir değeri kaldırın ve geri döndürün) ve 'push' (yığına bir değer itin) gibi birkaç önemli montaj dili talimatı ile değiştirilebilen bir bellek parçasıdır, ancak bir altyordam çağırın - bu adres yığına dönmek için iter) ve geri döner (altyordamdan geri döner - bu adres yığından çıkar ve atlar). Yığın işaretçi kaydının altındaki, gerektiğinde ayarlanabilen bellek bölgesidir. Yığın ayrıca, alt yordamlara argümanlar iletmek ve ayrıca alt yordamları çağırmadan önce kayıtlardaki değerleri korumak için kullanılır.

Yığın, işletim sistemi tarafından bir uygulamaya, tipik olarak bir sistem çağrısı benzeri malloc aracılığıyla verilen belleğin bir kısmıdır. Modern işletim sistemlerinde bu bellek, yalnızca çağrı sürecinin erişebildiği bir dizi sayfadır.

Yığının boyutu çalışma zamanında belirlenir ve genellikle program başlatıldıktan sonra büyümez. Bir C programında, yığının her işlev içinde bildirilen her değişkeni tutacak kadar büyük olması gerekir. Öbek gerektiğinde dinamik olarak büyüyecek, ancak işletim sistemi nihayetinde çağrı yapıyor (genellikle yığını malloc tarafından istenen değerden daha fazla artıracak, böylece en azından bazı gelecekteki mallokların çekirdeğe geri dönmesi gerekmeyecek Bu davranış genellikle özelleştirilebilir)

Programı başlatmadan önce yığını ayırdığınız için, yığını kullanabilmeniz için hiçbir zaman mallocation yapmanıza gerek yoktur, bu nedenle bu küçük bir avantajdır. Uygulamada, sanal bellek alt sistemlerine sahip modern işletim sistemlerinde neyin hızlı ve neyin yavaş olacağını tahmin etmek çok zordur, çünkü sayfaların nasıl uygulandığı ve nerede depolandığı bir uygulama detayıdır.


2
Burada ayrıca intel'in yığın erişimlerini, özellikle de bir işlevden nereye geri döndüğünüzü tahmin etmek gibi şeyleri büyük ölçüde optimize ettiğini belirtmek gerekir.
Tom Leys

113

Sanırım birçok insan size bu konuda çoğunlukla doğru cevaplar verdi.

Ancak kaçırılan bir ayrıntı, "yığın" ın aslında "serbest mağaza" olarak adlandırılması gerektiğidir. Bu ayrımın nedeni, orijinal serbest mağazanın "binom yığını" olarak bilinen bir veri yapısı ile uygulanmasıdır. Bu nedenle, malloc () / free () 'in erken uygulamalarından ayırma, bir öbekten ayırmadır. Bununla birlikte, bu modern günde, çoğu serbest mağaza, binom yığınları olmayan çok ayrıntılı veri yapıları ile uygulanmaktadır.


8
Başka bir nitpick - cevapların çoğu (hafifçe), Cdil tarafından bir "yığın" kullanılmasının gerekli olduğunu ima eder . Bu yaygın bir yanlış anlamadır, ancak (açık) uygulama için hakim olan paradigmadır C99 6.2.4 automatic storage duration objects(değişkenler). Aslında, "yığın" kelimesi C99dil standardında bile görünmüyor : open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf
johne 5

[@Heath] Cevabınız hakkında küçük bir yorumum var. Bu sorunun kabul edilen cevabına bir göz atın . O diyor ücretsiz mağaza büyük olasılıkla aynıdır yığın mutlaka olsa.
OmarOthman

91

Yığınla bazı ilginç şeyler yapabilirsiniz. Örneğin , bellek için yığını değil, özellikle yığını kullanan bir malloc biçimi olan alloca (kullanımıyla ilgili bol miktarda uyarıyı aşabileceğinizi varsayarak) gibi işlevleriniz vardır .

Yani, yığın tabanlı bellek hataları yaşadığım en kötü bazıları. Yığın belleği kullanırsanız ve ayrılan bloğunuzun sınırlarını aşarsanız, bir segment hatasını tetikleme şansınız yüksektir. (% 100 değil: bloğunuz daha önce ayırdığınız bir başkasıyla tesadüfen bitişik olabilir.) Ancak yığın üzerinde oluşturulan değişkenler her zaman birbiriyle bitişik olduğundan, sınırların dışında yazmak başka bir değişkenin değerini değiştirebilir. Programımın mantık yasalarına uymayı bıraktığını hissettiğimde, muhtemelen arabellek taşması olduğunu öğrendim.


Ne kadar taşınabilir alloca? Örneğin, Windows üzerinde çalışıyor mu? Sadece Unix benzeri işletim sistemleri için mi?
Peter Mortensen

89

Basitçe, yığın yerel değişkenlerin oluşturulduğu yerdir. Ayrıca, her alt programı çağırdığınızda, program sayacı (bir sonraki makine komutuna işaretçi) ve önemli kayıtlar ve bazen parametreler yığına itilir. Sonra altyordam içindeki herhangi bir yerel değişken yığına itilir (ve oradan kullanılır). Altyordam bittiğinde, tüm bu şeyler yığının dışına çıkar. PC ve kayıt verileri, geldiği gibi geri alınır ve geri getirilir, böylece programınız neşeli yoluna devam edebilir.

Yığın, dinamik bellek ayırmalarının (açık "yeni" veya "ayırma" çağrılarından) yapılan bellek alanıdır. Farklı boyutlardaki bellek bloklarını ve bunların tahsis durumlarını takip edebilen özel bir veri yapısıdır.

"Klasik" sistemlerde RAM, yığın işaretçisi hafızanın altından başlayacak, yığın işaretçisi en üstte başlayacak ve birbirlerine doğru büyüyecek şekilde yerleştirilmiştir. Eğer üst üste biniyorlarsa, RAM'iniz bitti demektir. Bu modern çok iş parçacıklı işletim sistemleri ile çalışmaz. Her iş parçacığının kendi yığını olmalıdır ve bunlar dinamik olarak oluşturulabilir.


[@TED] Neden "bazen parametreler yığına aktarılıyor" dediniz? Bildiğim şey, her zaman olduklarıdır. Daha fazla ayrıntı verebilir misiniz?
OmarOthman

1
@OmarOthman - Diyorum ki, derleyici / tercümanın yazarına tamamen bağlı olduğundan, bir alt program çağrıldığında ne olur. Klasik Fortran davranışı bir yığın kullanmamaktır. Bazı diller etkili bir şekilde metinsel bir ikame olan ad-adı gibi egzotik şeyleri destekler.
TED

83

WikiAnwser sitesinden.

yığın

Bir işlev veya yöntem, başka bir işlevi vb. Çağıran başka bir işlevi çağırdığında, tüm bu işlevlerin yürütülmesi, en son işlev değerini döndürene kadar askıda kalır.

Bu askıya alınmış işlev çağrıları zinciri yığadır, çünkü yığındaki öğeler (işlev çağrıları) birbirine bağlıdır.

Yığın, özel durum işleme ve iş parçacığı yürütmelerinde dikkate alınması önemlidir.

Yığın

Yığın, değişkenleri depolamak için programlar tarafından kullanılan bellektir. Yığın öğesinin (değişkenler) birbirine bağımlılığı yoktur ve her zaman her zaman rastgele erişilebilir.


"Kabul edilen cevabı daha da düşük seviyorum çünkü daha da düşük." Bu kötü bir şey, iyi bir şey değil.
Yörüngedeki Hafiflik Yarışları

54

yığın

  • Çok hızlı erişim
  • Değişkenleri açıkça ayırmak zorunda değilsiniz
  • Alan CPU tarafından verimli bir şekilde yönetilir, bellek parçalanmaz
  • Yalnızca yerel değişkenler
  • Yığın boyutu sınırı (işletim sistemine bağlı)
  • Değişkenler yeniden boyutlandırılamaz

Yığın

  • Değişkenlere global olarak erişilebilir
  • Bellek boyutunda sınır yok
  • (Nispeten) daha yavaş erişim
  • Alanın garantili verimli kullanımı yoktur, bellek blokları tahsis edildikten sonra hafıza serbest bırakılabilir, daha sonra serbest bırakılabilir
  • Belleği yönetmelisiniz (değişkenleri ayırmak ve boşaltmaktan sorumlusunuz)
  • Değişkenler realloc () kullanılarak yeniden boyutlandırılabilir

50

Tamam, basitçe ve kısaca, sıralı ve sıralı değil ...!

yığın : Yığın öğelerinde, işler birbirinin üstüne gelir, işlenmek için daha hızlı ve daha verimli olacağınız anlamına gelir! ...

Bu nedenle, belirli bir öğeyi işaret etmek için her zaman bir dizin vardır, ayrıca işlem daha hızlı olur, öğeler arasında da ilişki vardır! ...

Yığın : Sipariş yok, işlem daha yavaş olacak ve değerler belirli bir sipariş veya indeks olmadan berbat olacak ... rastgele var ve aralarında bir ilişki yok ... bu yüzden yürütme ve kullanım süresi değişebilir ...

Ayrıca nasıl göründüklerini göstermek için aşağıdaki resmi oluşturuyorum:

resim açıklamasını buraya girin


49

Kısacası

Statik bellek ayırma için bir yığın ve her ikisi de bilgisayarın RAM'inde depolanan dinamik bellek ayırma için bir yığın kullanılır.


Detayda

Yığın

Yığın, CPU tarafından oldukça yakından yönetilen ve optimize edilen bir "LIFO" (son giren ilk çıkar) veri yapısıdır. Bir işlev her yeni değişkeni bildirdiğinde, yığına "itilir". Daha sonra bir işlev her çıktığında, o işlev tarafından yığına itilen tüm değişkenler serbest bırakılır (yani, silinirler). Bir yığın değişkeni serbest bırakıldıktan sonra, o bellek bölgesi diğer yığın değişkenleri için kullanılabilir hale gelir.

Yığını değişkenleri depolamak için kullanmanın avantajı, belleğin sizin için yönetilmesidir. Belleği elle ayırmanız veya artık ihtiyacınız olmadığında boşaltmanız gerekmez. Dahası, CPU yığın belleğini çok verimli bir şekilde düzenlediğinden, yığın değişkenlerinden okuma ve yazma değişkenlerine yazma çok hızlıdır.

Daha fazlasını burada bulabilirsiniz .


Öbek

Yığın, bilgisayarınızın belleğinde sizin için otomatik olarak yönetilmeyen ve CPU tarafından sıkı bir şekilde yönetilmeyen bir bölgedir. Daha serbest yüzen bir hafıza bölgesidir (ve daha büyüktür). Öbek üzerinde bellek ayırmak için, yerleşik C işlevleri olan malloc () veya calloc () kullanmanız gerekir. Öbek üzerinde bellek ayırdıktan sonra, artık ihtiyacınız olmadığında o belleği yeniden ayırmak için free () kullanmaktan sorumlusunuz.

Bunu yapamazsanız, programınız bellek sızıntısı olarak bilinen şeye sahip olacaktır. Yani, öbek üzerindeki bellek hala bir kenara bırakılacak (ve diğer işlemler için kullanılamayacak). Hata ayıklama bölümünde göreceğimiz gibi, bellek sızıntılarını tespit etmenize yardımcı olabilecek Valgrind adlı bir araç var .

Yığın aksine, yığın değişken boyutta boyut kısıtlamaları yoktur (bilgisayarınızın belirgin fiziksel sınırlamaları dışında). Yığın belleğinin okunması ve yazılması biraz daha yavaştır, çünkü öbekteki belleğe erişmek için işaretçiler kullanmak zorundadır. Kısaca işaretçiler hakkında konuşacağız.

Yığın aksine, yığın üzerinde oluşturulan değişkenlere programınızın herhangi bir yerinde herhangi bir işlevle erişilebilir. Yığın değişkenleri temelde küreseldir.

Daha fazlasını burada bulabilirsiniz .


Yığına ayrılan değişkenler doğrudan belleğe kaydedilir ve bu belleğe erişim çok hızlıdır ve program derlendiğinde tahsisi ele alınır. Bir işlev veya yöntem sırayla başka bir işlevi vb. Çağıran başka bir işlevi çağırdığında, tüm bu işlevlerin yürütülmesi en son işlev değerini döndürene kadar askıda kalır. Yığın her zaman bir LIFO düzeninde ayrılır, en son ayrılmış blok her zaman serbest bırakılacak bir sonraki bloktur. Bu, yığını takip etmeyi gerçekten kolaylaştırır, yığıntan bir blok serbest bırakmak bir işaretçiyi ayarlamaktan başka bir şey değildir.

Öbek üzerinde ayrılan değişkenlerin bellekleri çalışma zamanında ayrılır ve bu belleğe erişmek biraz daha yavaştır, ancak yığın boyutu yalnızca sanal belleğin boyutu ile sınırlıdır. Yığın öğelerinin birbirine bağımlılığı yoktur ve her zaman her zaman rastgele erişilebilir. Bir bloğu istediğiniz zaman tahsis edebilir ve istediğiniz zaman serbest bırakabilirsiniz. Bu, herhangi bir zamanda yığının hangi bölümlerinin tahsis edildiğini veya serbest olduğunu izlemeyi çok daha karmaşık hale getirir.

Resim açıklamasını buraya girin

Derleme süresinden önce ne kadar veri ayırmanız gerektiğini tam olarak biliyorsanız ve çok büyük değilse yığını kullanabilirsiniz. Çalışma zamanında ne kadar veriye ihtiyacınız olacağını tam olarak bilmiyorsanız veya çok fazla veri ayırmanız gerekiyorsa öbeği kullanabilirsiniz.

Çok iş parçacıklı bir durumda, her iş parçacığının kendi bağımsız yığını vardır, ancak yığını paylaşırlar. Yığın iş parçacığına ve yığın uygulamaya özeldir. Yığın, özel durum işleme ve iş parçacığı yürütmelerinde dikkate alınması önemlidir.

Her iş parçacığı bir yığın alır, ancak uygulama için yalnızca bir yığın vardır (farklı ayırma türleri için birden çok yığın olması nadir olmamasına rağmen).

Resim açıklamasını buraya girin

Çalışma zamanında, uygulamanın daha fazla yığın gerektirmesi durumunda, boş bellekten bellek ayırabilir ve yığının belleğe ihtiyacı varsa, uygulama için boş bellekten ayrılan bellekten bellek ayırabilir.

Hatta burada ve burada daha fazla ayrıntı verilmektedir .


Şimdi sorunuzun cevaplarına gelin .

İşletim sistemi veya dil çalışma zamanı tarafından ne ölçüde kontrol ediliyorlar?

İş parçacığı oluşturulduğunda işletim sistemi yığını, sistem düzeyindeki her iş parçacığı için ayırır. Genellikle işletim sistemi, yığının uygulamaya tahsis edilmesi için dil çalışma zamanı tarafından çağrılır.

Daha fazlasını burada bulabilirsiniz .

Kapsamı nedir?

Zaten zirvede.

"Derleme süresinden önce ne kadar veri ayırmanız gerektiğini tam olarak biliyorsanız ve çok büyük değilse yığını kullanabilirsiniz. Çalışma zamanında ne kadar veriye ihtiyacınız olacağını tam olarak bilmiyorsanız veya çok fazla veri ayırmanız gerekiyor. "

Daha fazlasını burada bulabilirsiniz .

Her birinin boyutunu ne belirler?

Bir iş parçacığı oluşturulduğunda yığının boyutu OS tarafından ayarlanır . Yığın boyutu uygulama başlangıcında ayarlanır, ancak alan gerektiğinde büyüyebilir (ayırıcı işletim sisteminden daha fazla bellek ister).

Birini daha hızlı yapan nedir?

Yığın tahsisi çok daha hızlıdır çünkü gerçekten yaptığı tek şey yığın işaretçisini hareket ettirmektir. Bellek havuzlarını kullanarak, yığın tahsisinden karşılaştırılabilir performans elde edebilirsiniz, ancak bu biraz ek bir 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; ayrıca nesnelerin beklenen ömrü hakkında da çok şey anlatır.

Detaylar bulunabilir burada .


36

1980'lerde UNIX, büyük şirketlerin kendi şirketlerini devirmesiyle tavşanlar gibi yayıldı. Exxon, onlarca marka ismini kaybetmişti. Belleğin nasıl düzenlendiği birçok uygulayıcının takdirine bağlıydı.

Tipik bir C programı, brk () değerini değiştirerek artma fırsatı ile düz bir şekilde belleğe yerleştirildi. Tipik olarak, HEAP bu brk değerinin hemen altındaydı ve artan brk, mevcut yığın miktarını arttırdı.

Tekli STACK tipik olarak HEAP altında, bir sonraki sabit bellek bloğunun tepesine kadar hiçbir değer içermeyen bir bellek yolu olan bir alandır. Bu sonraki blok genellikle çağının ünlü hack'lerinden birinde yığın verileriyle yazılabilen CODE idi.

Tipik bir bellek bloğu, bir üreticinin teklifinde yanlışlıkla sıfırlanmayan BSS (sıfır değerli bir blok) idi. Diğeri, dizeler ve sayılar dahil olmak üzere başlangıç ​​değerlerini içeren DATA idi. Üçüncüsü, CRT (C çalışma zamanı), main, fonksiyonlar ve kütüphaneleri içeren CODE idi.

UNIX'te sanal belleğin ortaya çıkması, kısıtlamaların çoğunu değiştirir. Bu blokların bitişik veya sabit olması veya belirli bir şekilde sipariş edilmesinin nesnel bir nedeni yoktur. Elbette, UNIX'ten önce bu kısıtlamalardan muzdarip olmayan Multics vardı. İşte o dönemin hafıza düzenlerinden birini gösteren bir şema.

1980'lerin tipik bir UNIX C programı bellek düzeni



26

Birkaç sent: Bence, grafik ve daha basit bir bellek çizmek iyi olacak:

Bu, ne olduğunu daha kolay anlamak için sadeleştirilmiş işlem belleği oluşturma vizyonum


Oklar - büyüme yığını ve yığının, işlem yığını boyutunun OS'de tanımlandığı, genellikle iplikteki parametrelerle iş parçacığı yığın boyutu sınırlarının API oluşturduğunu gösterir. Öbek, genellikle 32 bit 2-4 GB için işlem maksimum sanal bellek boyutuyla sınırlanır.

Çok basit bir yol: işlem yığını, işlem ve genel olarak tüm iş parçacıkları için genel olarak, malloc () gibi bir şeyle bellek ayırma için kullanılır .

Yığın, işlev çağrısında parametreler, yerel işlev değişkenleri olarak işlenen, yaygın durumda işlev dönüş işaretçileri ve değişkenlerinde depolamak için hızlı bir bellektir.


23

Bazı cevaplar çürütüldüğünden, akarımı katkıda bulunacağım.

Şaşırtıcı bir şekilde, hiç kimse birden fazla (yani çalışan OS seviyesi iş parçacıklarının sayısıyla ilgili değil) çağrı yığınlarının yalnızca egzotik dillerde (PostScript) veya platformlarda (Intel Itanium) değil, aynı zamanda fiberlerde , yeşil iş parçacıklarında bulunacağından bahsetmedi. ve bazı koroutin uygulamaları .

Lifler, yeşil iplikler ve koroutinler birçok yönden benzerdir, bu da çok karışıklığa yol açar. Lifler ve yeşil iplikler arasındaki fark, birincisinin kooperatif çoklu görevini kullanmasıdır, ikincisi ise kooperatif veya önleyici olanı (hatta her ikisini de) içerebilir. Lifler ve koroutinler arasındaki ayrım için buraya bakın .

Her halükarda, hem fiberlerin, hem yeşil ipliklerin hem de koroutinlerin amacı, tek bir OS seviyesi iplik içinde eşzamanlı olarak çalışan birden fazla işleve sahip olmakla birlikte paralel değildir ( ayrım için bu SO sorusuna bakın ) ve kontrolü birbirinden ileri geri aktarır organize bir şekilde.

Elyaf, yeşil iplik veya koroutin kullanırken, genellikle işlev başına ayrı bir yığına sahip olursunuz . (Teknik olarak, yalnızca bir yığın değil, tüm yürütme bağlamı işlev başınadır. En önemlisi, CPU kayıtları.) Her iş parçacığı için aynı anda çalışan işlevler kadar çok yığın vardır ve iş parçacığı her bir işlevi yürütme arasında geçiş yapar programınızın mantığına göre. Bir işlev sonuna geldiğinde, yığını yok edilir. Bu nedenle, yığınların sayısı ve ömürleri dinamiktir ve işletim sistemi düzeyindeki iş parçacıklarının sayısı ile belirlenmez!

" Genelde fonksiyon başına ayrı bir yığın var " dedim . Her iki Orada konum stackful ve stackless couroutines ait uygulamalar. En önemli stackful C ++ uygulaması şunlardır Boost.Coroutine ve Microsoft PPL 'ler async/await. (Bununla birlikte, C ++ 17'ye önerilen C ++ 'ın sürdürülebilen işlevleri (aka " asyncve await"), yığınsız koroutinleri kullanması muhtemeldir.)

C ++ standart kütüphanesine fiber önerisi yakında sunulacaktır. Ayrıca, bazı üçüncü taraf kütüphaneleri var . Yeşil iplikler Python ve Ruby gibi dillerde son derece popülerdir.


19

Önemli noktalar zaten kapsanmasına rağmen, paylaşacak bir şeyim var.

yığın

  • Çok hızlı erişim.
  • RAM'de saklanır.
  • İşlev çağrıları, burada iletilen yerel değişkenler ve işlev parametreleriyle birlikte yüklenir.
  • Program kapsam dışına çıktığında alan otomatik olarak boşaltılır.
  • Sıralı bellekte saklanır.

Yığın

  • Stack'e nispeten yavaş erişim.
  • RAM'de saklanır.
  • Dinamik olarak oluşturulan değişkenler burada saklanır ve daha sonra kullanımdan sonra ayrılan belleğin boşaltılmasını gerektirir.
  • Bellek tahsisinin yapıldığı her yerde saklanır, her zaman işaretçiyle erişilir.

İlginç not:

  • İşlev çağrıları yığın halinde depolanmış olsaydı, 2 dağınık noktaya neden olurdu:
    1. Yığında sıralı depolama nedeniyle yürütme daha hızlıdır. Yığın halinde depolama, büyük zaman tüketimine neden olur, böylece tüm programın daha yavaş çalışmasını sağlar.
    2. İşlevler yığın halinde depolanmış olsaydı (işaretçi tarafından gösterilen dağınık depolama), arayan adresine geri dönmenin bir yolu olmazdı (bu yığın bellekte sıralı depolama nedeniyle oluşur).

1
kısa ve temiz. nice :)
ingconti

13

Vaov! Pek çok cevap ve ben bunlardan birinin doğru yaptığını sanmıyorum ...

1) Nerede ve ne (fiziksel olarak gerçek bir bilgisayarın hafızasında)?

Yığın, program görüntünüze tahsis edilen en yüksek bellek adresi olarak başlayan bellektir ve daha sonra oradan değeri düşer. Denilen işlev parametreleri ve işlevlerde kullanılan tüm geçici değişkenler için ayrılmıştır.

İki yığın vardır: kamu ve özel.

Özel yığın, programınızdaki son kod baytından sonra 16 baytlık bir sınırda (64 bit programlar için) veya 8 baytlık bir sınırda (32 bit programlar için) başlar ve oradan değer artar. Varsayılan yığın olarak da adlandırılır.

Özel yığın çok büyürse, yığın alanı üst üste bindirilir ve yığın çok büyük hale gelirse yığın üst üste gelir. Yığın daha yüksek bir adreste başladığı ve daha düşük adrese doğru ilerlediği için, uygun hackleme ile yığını o kadar büyük hale getirebilirsiniz ki özel yığın alanını aşacak ve kod alanını üst üste bindirecektir. Bu durumda, hile, koda takabileceğiniz kod alanının yeterince üst üste gelmesidir. Yapması biraz zor ve bir program çökmesi riskiyle karşı karşıyasınız, ancak kolay ve çok etkili.

Genel yığın, program görüntü alanınızın dışındaki kendi bellek alanında bulunur. Bellek kaynakları az kalırsa sabit diske sifonlanacak olan bu bellektir.

2) İşletim sistemi veya dil çalışma süresi tarafından ne ölçüde kontrol ediliyorlar?

Yığın programcı tarafından denetlenir, özel yığın işletim sistemi tarafından yönetilir ve genel yığın, bir işletim sistemi hizmeti olduğu için kimse tarafından denetlenmez - istekte bulunursunuz ya da verilir veya reddedilir.

2b) Kapsamı nedir?

Hepsi program için küreseldir, ancak içerikleri özel, genel veya genel olabilir.

2c) Her birinin boyutunu ne belirler?

Yığının ve özel yığının boyutu derleyici çalışma zamanı seçenekleriniz tarafından belirlenir. Ortak yığın çalışma zamanında bir boyut parametresi kullanılarak başlatılır.

2d) Birini daha hızlı yapan nedir?

Hızlı olacak şekilde tasarlanmamıştır, kullanışlı olacak şekilde tasarlanmıştır. Programcının bunları nasıl kullandığı "hızlı" mı yoksa "yavaş" mı olduğunu belirler

REF:

https://norasandler.com/2019/02/18/Write-a-Compiler-10.html

https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-getprocessheap

https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapcreate


8

Birçok cevap kavram olarak doğrudur, ancak alt yordamların (montaj dilinde CALL ..) çağrılmasına izin vermek için donanım (yani mikroişlemci) tarafından bir yığının gerekli olduğuna dikkat etmeliyiz. (OOP adamları buna yöntem diyecektir )

Yığında, iade adreslerini kaydedersiniz ve arama → push / ret → pop doğrudan donanımda yönetilir.

Yığını parametreleri iletmek için kullanabilirsiniz .. kayıtlar kullanmaktan daha yavaş olsa bile (bir mikroişlemci guru der ya da iyi bir 1980'lerin BIOS kitabı ...)

  • Yığın olmadan hiçbir mikroişlemci çalışamaz. (bir programı, alt programlar / fonksiyonlar olmadan montaj dilinde bile hayal edemiyoruz)
  • Öbek olmadan yapabilir. (Bir derleme dili programı, yığın bir işletim sistemi kavramı, malloc olarak bir işletim sistemi / Lib çağrısı olmadan çalışabilir.

Yığın kullanımı şu şekilde daha hızlıdır:

  • Donanım ve push / pop bile çok verimlidir.
  • malloc, çekirdek moduna girmeyi gerektirir, bazı kodları yürütmek için kilit / semafor (veya diğer senkronizasyon ilkellerini) kullanın ve ayırmayı izlemek için gereken bazı yapıları yönetin.

OPP nedir? Şunu mu demek istediniz: OOP ( nesne yönelimli_programlama )?
Peter Mortensen

Bunun mallocbir çekirdek çağrısı olduğunu mu demek istiyorsun ?
Peter Mortensen

1) evet, üzgünüm .. OOP ... 2) malloc: Kısaca yazıyorum, üzgünüm ... malloc kullanıcı alanında .. ama diğer çağrıları tetikleyebilir .... nokta yığın kullanmak çok yavaş olabilir ...
ingconti

Msgstr " Birçok cevap kavram olarak doğrudur, ancak alt yordamların (montaj dilinde CALL ..) çağrılmasına izin vermek için donanım (yani mikroişlemci) tarafından bir yığının gerekli olduğuna dikkat etmeliyiz ". CPU yığınını (modern CPU'da bir tane varsa) ve dil çalışma zamanı yığınlarını (iş parçacığı başına bir tane) karıştırıyorsunuz. Programcılar bir yığın hakkında konuştuğunda, bu çalışma zamanının iş parçacığı yürütme yığınıdır, örneğin bir NET iş parçacığı yığını), CPU yığını hakkında konuşmuyoruz.
dakika

1

Yığın, öğelerini basitçe bir yığın olarak yöneten, erişimi kolay bir bellektir. Yalnızca boyutun önceden bilindiği öğeler yığına gidebilir . Sayılar, dizgiler, booleanlar için durum böyledir.

Yığın Tam boyutu ve yapısı önceden belirlemek edemez hangi öğeler için bir hafızadır . Nesneler ve diziler çalışma zamanında mutasyona uğratılabildiğinden ve değişebildiğinden, yığına girmeleri gerekir.

Kaynak: Academind


0

Gerçekten iyi bir tartışma için teşekkür ederim ama gerçek bir çaylak olarak talimatların nerede saklandığını merak ediyorum? BAŞLANGIÇ'ta bilim adamları iki mimar arasında karar vermişlerdi (von NEUMANN, her şeyin VERİ ve HARVARD olarak kabul edildiği yerlerde bellek alanı talimatlar için ayrılmış, diğeri veri için). Nihayetinde, von Neumann tasarımı ile gittik ve şimdi her şey 'aynı' olarak kabul ediliyor. Https://www.cs.virginia.edu/~evans/cs216/guides/x86.html derlemesini öğrenirken bu benim için zor oldu çünkü kayıtlar ve yığın işaretçileri hakkında konuşuyorlar.

Yukarıdaki her şey DATA hakkında konuşuyor. Benim tahminim, bir talimatın belirli bir bellek ayak izi ile tanımlanmış bir şey olduğu için, yığının üzerine gideceği ve böylece derlemede tartışılan tüm 'bu' kayıtların yığının üzerinde olacağıdır. Tabii daha sonra talimatlar ve veriler ile nesne odaklı programlama geldi dinamik bir yapıya geldi, böylece şimdi talimatlar da yığın üzerinde kalacaktı?

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.