Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
193 views
in Technique[技术] by (71.8m points)

java - OutOfMemoryError when seemingly unrelated code block commented out

Could someone explain why this program throws a OutOfMemoryError when the for loop is commented out? If it is uncommented this runs fine.

The exception thrown is:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

public class JavaMemoryPuzzlePolite
{
    private final int dataSize = (int)(Runtime.getRuntime().maxMemory()* 0.6);

    public void f()
    {
        {
            System.out.println(dataSize);
            byte[] data = new byte[dataSize];
        }

        /*
        for(int i = 0; i < 10; i++) {
            System.out.println("Please be so kind and release memory");
        }
        */

        System.out.println(dataSize);
        byte[] data2 = new byte[dataSize];
    }

    public static void main(String []args)
    {
        JavaMemoryPuzzlePolite jmp = new JavaMemoryPuzzlePolite();
        jmp.f();
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I've investigated this with many different types of code snippets that can be inserted where your comment is and the only type of code that will not cause an OutOfMemoryError is code that assigns some value to a local variable.

This is the explanation that makes the most sense to me:

When you have

byte[] data = new byte[dataSize];

The bytecode instructions are

    12: newarray       byte
    14: astore_1 

Where newarray creates a new array and astore_1 stores a reference to it in a local variable 1.

After this, the scope of that variable is lost, but the bytecode doesn't say anything about its value being cleared, so there's a reference to that object remaining in the stack frame. This specific garbage collector deems it reachable even if the code itself cannot reach it.

If instead you try to assign another local variable, like

byte i = 1;

then the corresponding byte code instructions are something like

    15: iconst_1      
    16: istore_1   

where iconst_1 stores the value 1 on the stack and istore_1 stores that value in the variable 1, which seems to be the same variable as before. If it is, then you are overwriting its value, the reference to the byte[] object is lost, and the object then "becomes" eligible for garbage collection.

The final proof

Compile this code with the -g option

public class Driver {
    private static final int dataSize = (int) (Runtime.getRuntime().maxMemory() * 0.6);

    public static void main(String[] args) throws InterruptedException {
        {
            System.out.println(dataSize);
            byte[] data = new byte[dataSize];

        }
        byte i = 1;
        System.out.println(dataSize);
        byte[] data2 = new byte[dataSize];
    }

}

and then run javap -c -l Driver. You will see a LocalVariableTable like so

LocalVariableTable:
  Start  Length  Slot  Name   Signature
   15       0     1    data   [B
    0      33     0    args   [Ljava/lang/String;
   17      16     1     i     B
   32       1     2    data2  [B

where slot is the index in astore_1 and istore_1. So you see, the reference to the byte[] is cleared when you assign a new value to the local variable. Even if the variables have different types/names, in bytecode, they are stored in the same place.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...