Java yığın boyutu nasıl artırılır?


123

Bu soruyu JVM'de çalışma zamanı çağrı yığını boyutunu nasıl artıracağımı öğrenmek için sordum. Buna bir cevabım var ve ayrıca Java'nın büyük bir çalışma zamanı yığınının gerekli olduğu durumu nasıl ele aldığıyla ilgili birçok yararlı cevabım ve yorumum var. Sorumu cevapların özeti ile genişlettim.

Başlangıçta JVM yığın boyutunu artırmak istedim, böylece program gibi programlar bir StackOverflowError.

public class TT {
  public static long fact(int n) {
    return n < 2 ? 1 : n * fact(n - 1);
  }
  public static void main(String[] args) {
    System.out.println(fact(1 << 15));
  }
}

Karşılık gelen yapılandırma ayarı, java -Xss...yeterince büyük bir değere sahip komut satırı bayrağıdır. TTYukarıdaki program için, OpenJDK'nin JVM'si ile şu şekilde çalışır:

$ javac TT.java
$ java -Xss4m TT

Cevaplardan biri, -X...bayrakların uygulamaya bağlı olduğuna da işaret etti . Kullanıyordum

java version "1.6.0_18"
OpenJDK Runtime Environment (IcedTea6 1.8.1) (6b18-1.8.1-0ubuntu1~8.04.3)
OpenJDK 64-Bit Server VM (build 16.0-b13, mixed mode)

Yalnızca bir iş parçacığı için büyük bir yığın belirtmek de mümkündür (nasıl yapılacağına ilişkin yanıtlardan birine bakın). Bu, java -Xss...ihtiyaç duymayan iş parçacıkları için bellek israfını önlemek için tavsiye edilir .

Yukarıdaki programın tam olarak ne kadar büyük bir yığına ihtiyacı olduğunu merak ediyordum, bu yüzden çalıştırdım n:

  • -Xss4m yeterli olabilir fact(1 << 15)
  • -Xss5m yeterli olabilir fact(1 << 17)
  • -Xss7m yeterli olabilir fact(1 << 18)
  • -Xss9m yeterli olabilir fact(1 << 19)
  • -Xss18m yeterli olabilir fact(1 << 20)
  • -Xss35m yeterli olabilir fact(1 << 21)
  • -Xss68m yeterli olabilir fact(1 << 22)
  • -Xss129m yeterli olabilir fact(1 << 23)
  • -Xss258m yeterli olabilir fact(1 << 24)
  • -Xss515m yeterli olabilir fact(1 << 25)

Yukarıdaki sayılardan, Java'nın yukarıdaki işlev için yığın çerçevesi başına yaklaşık 16 bayt kullandığı görülmektedir ki bu mantıklıdır.

Yukarıdaki numaralandırma yeterli yerine yeterli olabilir , çünkü yığın gereksinimi belirleyici değildir: aynı kaynak dosyayla birden çok kez çalıştırmak ve aynı -Xss...bazen başarılı olur ve bazen bir StackOverflowError. Örneğin 1 << 20 için, -Xss18m10 üzerinden 7 koşuda yeterliydi ve -Xss19mher zaman da yeterli değildi, ancak -Xss20myeterliydi (100 üzerinden 100 koşunun tamamında). Çöp toplama, JIT devreye girme veya başka bir şey bu belirleyici olmayan davranışa neden oluyor mu?

Bir StackOverflowError(ve muhtemelen diğer istisnalarda) yazdırılan yığın izleme , çalışma zamanı yığınının yalnızca en son 1024 öğesini gösterir. Aşağıdaki yanıt, ulaşılan tam derinliğin nasıl hesaplanacağını gösterir (bu, 1024'ten çok daha büyük olabilir).

Yanıt veren birçok kişi, aynı algoritmanın alternatif, daha az yığın-aç uygulamalarını düşünmenin iyi ve güvenli bir kodlama uygulaması olduğuna dikkat çekti. Genel olarak, bir dizi özyinelemeli işlevi yinelemeli işlevlere dönüştürmek mümkündür (örneğin Stack, çalışma zamanı yığını yerine öbek üzerinde doldurulan bir nesne kullanarak ). Bu özel factişlev için onu dönüştürmek oldukça kolaydır. Yinelemeli sürümüm şöyle görünür:

public class TTIterative {
  public static long fact(int n) {
    if (n < 2) return 1;
    if (n > 65) return 0;  // Enough powers of 2 in the product to make it (long)0.
    long f = 2;
    for (int i = 3; i <= n; ++i) {
      f *= i;
    }
    return f;
  }
  public static void main(String[] args) {
    System.out.println(fact(1 << 15));
  }
}

Bilginize, yukarıdaki yinelemeli çözümün gösterdiği gibi, factişlev 65'in üzerindeki sayıların (aslında, 20'nin üzerinde bile) tam faktöriyelini hesaplayamaz, çünkü yerleşik Java türü longtaşabilir. Şekilde elden factgeri dönmek olan bir çok BigIntegeryerine longbüyük girişler için kesin sonuçlar da olacaktır.


Olduğundan daha basit görünüyor. fact (), yinelemeli olarak 32K kez çağrılır. Bu, 1MB'den az yığın olmalıdır. : - /
Aaron Digulla

@Aaron: + Çok fazla fonksiyon ek yükü
halfdan

4
Yığın sorunlarının dışında. Uzunluğunuzu ve içlerinizi uçurduğunuzu unutmayın. 1 << 4, negatif gitmeden önce kullanabileceğim ve sonra 0'a geçmeden önce kullanabileceğim maksimum değerdir. BigInteger kullanmayı deneyin
Sean

Yine de, fonksiyon ek yükünün gerçekten bu kadar olduğundan emin değilim - bence yine de birkaç megabaytlık yığın alanı sırasıyla 2 ^ 15 çağrı yapabilmelisiniz.
Neil Coffey

7
Not: Her iş parçacığının yığın boyutunu ayarlıyorsunuz ve anlamsız bir sonuç üretiyorsunuz, hepsi tek bir kod satırını yeniden düzenlemekten kaçınmak için. Önceliklerinizi belirlediğinize sevindim. : P
Peter Lawrey

Yanıtlar:


78

Hmm ... benim için çalışıyor ve 999 MB'tan çok daha az yığınla:

> java -Xss4m Test
0

(Windows JDK 7, derleme 17.0-b05 istemci VM ve Linux JDK 6 - yayınladığınız sürüm bilgilerinin aynısı)


1
Muhtemelen yorumum içindi, Neil'in gönderdiği şeyin aynısını fark ettiğimde onu sildim.
Sean

Bu soru ve cevabınız sayesinde ödevimi tamamlamayı başardım. DFS işlevimin ~ 10 ^ 5 köşeli bir grafikte yinelenmesi gerekiyordu. Sonunda -Xss129m: D
bholagabbar

11

Yığın izlemesinde yinelenen çizgilerle "1024 derinliğini" hesapladığınızı varsayıyorum.

Açıkçası, Throwable'daki yığın izleme dizisi uzunluğu 1024 ile sınırlı görünüyor. Aşağıdaki programı deneyin:

public class Test {

    public static void main(String[] args) {

        try {
            System.out.println(fact(1 << 15));
        }
        catch (StackOverflowError e) {
            System.err.println("true recursion level was " + level);
            System.err.println("reported recursion level was " +
                               e.getStackTrace().length);
        }
    }

    private static int level = 0;
    public static long fact(int n) {
        level++;
        return n < 2 ? n : n * fact(n - 1);
    }
}

9

İş parçacığı yığın boyutu ile oynamak istiyorsanız, Hotspot JVM'de -Xss seçeneğine bakmak isteyeceksiniz. JVM'nin -X parametreleri dağıtıma özel IIRC olduğundan, Hotspot olmayan VM'lerde farklı bir şey olabilir.

Hotspot'ta, java -Xss16M16 megapiksellik bir boyut yapmak istiyorsanız bu gibi görünür .

Tip java -X -helpEğer geçirebilirsiniz dağıtım özgü JVM tüm parametreleri görmek istiyorsanız. Bu diğer JVM'lerle aynı çalıştığından emin eğer değilim ama Hotspot özgü tüm parametreleri yazdırır.

Ne olursa olsun - Java'da yinelemeli yöntemler kullanımınızı sınırlandırmanızı tavsiye ederim. Bunları optimize etmede çok iyi değil - JVM, kuyruk özyinelemeyi desteklemiyor (bkz . JVM, kuyruk arama optimizasyonlarını engelliyor mu? ). Özyinelemeli yöntem çağrıları yerine bir while döngüsü kullanmak için yukarıdaki faktör kodunuzu yeniden düzenlemeyi deneyin.


8

Süreç içindeki yığın boyutunu kontrol etmenin tek yolu yeni bir Thread. Ancak, -Xssparametre ile kendi kendini çağıran bir alt Java işlemi oluşturarak da kontrol edebilirsiniz .

public class TT {
    private static int level = 0;

    public static long fact(int n) {
        level++;
        return n < 2 ? n : n * fact(n - 1);
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(null, null, "TT", 1000000) {
            @Override
            public void run() {
                try {
                    level = 0;
                    System.out.println(fact(1 << 15));
                } catch (StackOverflowError e) {
                    System.err.println("true recursion level was " + level);
                    System.err.println("reported recursion level was "
                            + e.getStackTrace().length);
                }
            }

        };
        t.start();
        t.join();
        try {
            level = 0;
            System.out.println(fact(1 << 15));
        } catch (StackOverflowError e) {
            System.err.println("true recursion level was " + level);
            System.err.println("reported recursion level was "
                    + e.getStackTrace().length);
        }
    }

}

Bu bilgilendirici cevap için teşekkürler, ek olarak seçenekler hakkında bilgi sahibi olmak güzel java -Xss....
pts

1
Bu konuda heyecanlandım, ancak daha sonra docs.oracle.com/javase/6/docs/api/java/lang/Thread.html#Thread - yığın oluşturma yapıcısı - okuduktan sonra heyecan gitti.
kellogs

Belgede sadece - "Bazı platformlarda" dediğinde hangi platformlar olduklarını merak ediyorum
Dennis C

3

Bu seçeneği ekleyin

--driver-java-options -Xss512m

kıvılcım gönder komutunuz bu sorunu çözecektir.


2

Tüm aklı başında yaklaşımlardan kaçınmaya istekli olduğunuz için mantıklı bir çözüm vermek zordur. Bir satır kodu yeniden düzenlemek mantıklı bir çözümdür.

Not: -Xss kullanımı her iş parçacığının yığın boyutunu belirler ve çok kötü bir fikirdir.

Başka bir yaklaşım, kodu aşağıdaki gibi değiştirmek için bayt kodu manipülasyonudur;

public static long fact(int n) { 
    return n < 2 ? n : n > 127 ? 0 : n * fact(n - 1); 
}

n> 127 için verilen her cevap 0'dır. Bu, kaynak kodunu değiştirmekten kaçınır.


1
Yüksek yığın boyutu belirlemenin, buna ihtiyaç duymayan iş parçacıkları için belleği boşa harcayacağına işaret ettiğiniz için teşekkür ederiz. Ayrıca, söz konusu factişlevin çok daha az yığın alanı kullanmak üzere yeniden düzenlenebileceğine işaret ettiğiniz için teşekkür ederiz .
pts

1
@pts, teşekkürleriniz not edildi. Çok daha karmaşık bir kullanım durumunda bunun mantıklı bir soru olduğunu düşünüyorum, ancak bunlar çok nadirdir.
Peter Lawrey

0

Tuhaf! 1 << 15 derinlikte bir özyineleme oluşturmak istediğinizi söylüyorsunuz ??? !!!!

Denemeyin öneririm. Yığının boyutu olacaktır 2^15 * sizeof(stack-frame). Yığın çerçeve boyutunun ne olduğunu bilmiyorum ama 2 ^ 15, 32.768. Oldukça fazla ... Eğer 1024'te (2 ^ 10) durursa, 2 ^ 5 kat daha büyük yapmanız gerekecek, gerçek ayarınızdan 32 kat daha büyük.



0

Anagram egzersizi yaptım , bu Sayım Değişim problemi gibi ama 50.000 değerle (madeni para). Yinelemeli yapılabileceğinden emin değilim , umrumda değil. Sadece -xss seçeneğinin bir etkisi olmadığını biliyorum - 1024 yığın çerçevesinden sonra her zaman başarısız oldum (scala, java'ya veya printStackTrace sınırlamasına teslim ederken kötü iş yapıyor olabilir. Bilmiyorum). Yine de açıklandığı gibi bu kötü bir seçenektir. Uygulamadaki tüm konu başlıklarının canavarca olmasını istemezsiniz. Ancak, yeni Thread (yığın boyutu) ile bazı deneyler yaptım. Bu gerçekten işe yarıyor,

  def measureStackDepth(ss: Long): Long = {
    var depth: Long = 0
      val thread: Thread = new Thread(null, new Runnable() {
        override def run() {
          try {
          def sum(n: Long): Long = {depth += 1; if (n== 0) 0 else sum(n-1) + 1}
          println("fact = " + sum(ss * 10))
          } catch {
            case e: StackOverflowError => // eat the exception, that is expected
          }
        }
      }, "deep stack for money exchange", ss)
      thread.start()
      thread.join()
    depth
  }                                               //> measureStackDepth: (ss: Long)Long


  for (ss <- (0 to 10)) println("ss = 10^" +  ss + " allows stack of size " -> measureStackDepth((scala.math.pow (10, ss)).toLong) )
                                                  //> fact = 10
                                                  //| (ss = 10^0 allows stack of size ,11)
                                                  //| fact = 100
                                                  //| (ss = 10^1 allows stack of size ,101)
                                                  //| fact = 1000
                                                  //| (ss = 10^2 allows stack of size ,1001)
                                                  //| fact = 10000
                                                  //| (ss = 10^3 allows stack of size ,10001)
                                                  //| (ss = 10^4 allows stack of size ,1336)
                                                  //| (ss = 10^5 allows stack of size ,5456)
                                                  //| (ss = 10^6 allows stack of size ,62736)
                                                  //| (ss = 10^7 allows stack of size ,623876)
                                                  //| (ss = 10^8 allows stack of size ,6247732)
                                                  //| (ss = 10^9 allows stack of size ,62498160)

Gördüğünüz gibi, iş parçacığına tahsis edilen katlanarak daha fazla yığınla katlanarak derinleşebilir.

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.