简体   繁体   中英

Redundant method call reduces available stack memory

I met StackOverflowError in my real project and made simple model that shows the problem. It's test class that calls some recursive method and saves the depth of error.

public class Main {
    static int c = 0;

    public static void main(String[] args) {
        long sum = 0;
        int exps = 100;
        for (int i = 0; i < exps; ++i) {
            c = 0;
            try {
                simpleRecursion();
            } catch (StackOverflowError e) {
                sum += c;
            }
        }
        System.out.println("Average method call depth: " + (sum / exps));
    }

    public static void simpleRecursion() {
        simpleMethod();
        ++c;
        simpleRecursion();
    }
}

There are two versions of simpleMethod:

public static void simpleMethod() {
}
  1. It gets either 51K or 59K method calls' depth in tests.
public static void simpleMethod() {
    c += 0;
}
  1. It gets either 48K or 58K method calls' depth in tests.

Why have these realizations got different results? I can't understand what extra data lies in stack in the second case. In my opinion simpleMethod should not influence stack memory because it's not in call chain.

The issue you experience might be related to inlining methods by the JVM due to performance reasons. Inlining a method might have an affect on the allocated stack size for that method. You can check with javap -v how big the stack size is for a method, which gets allocated when the method is called. For your code the result of javap -v is as follow:

The simpleRecursion() method:

  public static void simpleRecursion();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=0, args_size=0
         0: invokestatic  #13                 // Method simpleMethod:()V
         3: getstatic     #2                  // Field c:I
         6: iconst_1
         7: iadd
         8: putstatic     #2                  // Field c:I
        11: invokestatic  #3                  // Method simpleRecursion:()V
        14: return
      LineNumberTable:
        line 19: 0
        line 20: 3
        line 21: 11
        line 22: 14

The simpleMethod() method without the c+=0; line:

  public static void simpleMethod();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=0, args_size=0
         0: return
      LineNumberTable:
        line 25: 0

The simpleMethod(); method with the c+=0; line:

  public static void simpleMethod();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=0, args_size=0
         0: getstatic     #2                  // Field c:I
         3: iconst_0
         4: iadd
         5: putstatic     #2                  // Field c:I
         8: return
      LineNumberTable:
        line 25: 0
        line 26: 8

The method variant with the empty body requires a stack size of 0 , where the method variant with the c+=0; line requires a stack size of 2 .

My guess it that when the method simpleMethod() gets inlined into simpleRecursion() by the JVM/JIT/HotSpot (see other questions like Are there inline functions in java? or Would Java inline method(s) during optimization? ) it will increase the stack size of simpleRecursion() to make room for the required extra stack size of simpleMethod() . Now the stack size of simpleRecursion() is bigger, which results in hitting the limit with a StackOverflowError earlier.

Unfortunately, I cannot verify this since the JIT/HotSpot is involved. Heck, even running the same application multiple times results in different values for c at the end. And when I try it with a simpleRecursion() variant where the c+=0;is used instead of the method call to simpleMethod(); , the stack size stays the same, most likely because the compiler is smart enough to work with the same stack size of 2 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM