Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OutOfMemoryError: Java heap space when casting a numeric primitive to char

I have been studying Decorator pattern and developed simple class ToUpperCaseInputStream. I overrode read() method so it could convert all chars from InputStream to uppercase. Code of the method is shown below (throws OutOfMemoryError):

@Override
public int read() throws IOException {
    return Character.toUpperCase((char)super.read());
}

As I figured out later, casting to char is redundant, but it's not the point. I'm having "java.lang.OutOfMemoryError: Java heap space" when the code:

((char) super.read())  

evaluates. To make this simpler I wrote the same method (this one throws OutOfMemoryError):

@Override
public int read() throws IOException {
    int c =(char) super.read();
    return (c == -1 ? c : Character.toUpperCase(c));
} 

And this one does not:

@Override
public int read() throws IOException {
    int c = super.read();
    return (c == -1 ? c : Character.toUpperCase(c));
} 

When I remove casting from the assignment the code runs with no errors and results in all text uppercased. As it's said at Oracle tutorials:

An assignment to an array component of reference type (§15.26.1), a method invocation expression (§15.12), or a prefix or postfix increment (§15.14.2, §15.15.1) or decrement operator (§15.14.3, §15.15.2) may all throw an OutOfMemoryError as a result of boxing conversion (§5.1.7).

It seems that autoboxing is used, but as for me it's not the case. Both variants of the same method result in OutOfMemoryError. If I am wrong, please explain this to me, because it will blow up my head.

To provide more info there is the client code:

public class App {
public static void main(String[] args) throws IOException {

    try (InputStream inet = new ToUpperCaseInputStream(new FileInputStream("d:/TEMP/src.txt"));
        FileOutputStream buff = new FileOutputStream("d:/TEMP/dst.txt")) {
        copy(inet, buff);
    }
}

public static void copy(InputStream src, OutputStream dst) throws IOException {
    int elem;
    while ((elem = src.read()) != -1) {
        dst.write(elem);
    }
}

}

What it does is just prints simple message from one file to another.

Although the case is solved I want to share a really good explanation of how casting is done. https://stackoverflow.com/a/24637624/1923644

like image 430
Zarial Avatar asked Jan 17 '15 19:01

Zarial


1 Answers

Before you cast to char you need to check for -1 (signaling end of input data).

char in Java is an unsigned short, meaning that when -1 is returned, your cast will make it 65535. Even if you did not have OutOfMemory, your code is still broken.

Regarding why you are getting OOM error, it is hard to say without full code, maybe, later in the code there are some memory allocations based on value of the character.

So try this and see if it helps:

@Override
public int read() throws IOException {
    int c = super.read();
    if (c == -1) return c;

    char ch = (char) c;
    return Character.toUpperCase(ch);
} 
like image 183
yurgis Avatar answered Oct 09 '22 13:10

yurgis