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. TT
Yukarı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, -Xss18m
10 üzerinden 7 koşuda yeterliydi ve -Xss19m
her zaman da yeterli değildi, ancak -Xss20m
yeterliydi (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 fact
iş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, fact
işlev 65'in üzerindeki sayıların (aslında, 20'nin üzerinde bile) tam faktöriyelini hesaplayamaz, çünkü yerleşik Java türü long
taşabilir. Şekilde elden fact
geri dönmek olan bir çok BigInteger
yerine long
büyük girişler için kesin sonuçlar da olacaktır.