Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Memory allocation for an Exception

Can you please explain, where the instances of Exception or it's children are allocated in memory? Is it heap or stack, or something else?

Thanks!

like image 273
jdevelop Avatar asked May 06 '11 08:05

jdevelop


People also ask

How do I fix memory allocation error?

Select Advanced system settings and then select Settings in the Performance section on the Advanced tab. Select the Advanced tab, and then select Change in the Virtual memory section. Clear the Automatically manage paging file size for all drives check box.

Which exception is thrown when you fails to allocate memory?

std::bad_alloc is a type of exception that occurs when the new operator fails to allocate the requested space. This type of exception is thrown by the standard definitions of ​operator new (declaring a variable) and operator new[] (declaring an array) when they fail to allocate the requested storage space.

What is the allocation of memory?

Memory allocation is the process of reserving a partial or complete portion of computer memory for the execution of programs and processes. Memory allocation is achieved through a process known as memory management.

What are the different types of memory allocations?

There are two types of memory allocations. Static and dynamic.


1 Answers

For most JVM's all Object are created on the heap and Exception is not an exception. ;)

A JVM could allocate objects on the stack using Escape Analysis, however this is usually limited to objects which are used only in one method and not returned. i.e. it is highly unlikely that Exceptions would be a good candidate.


Something which is special about the way Throwables (incl Exception) are created on many JVMs is that the stack trace elements are not created until they are needed. This is because most of the time they are not needed and they are expensive to create. However the information to create the stack trace is retained some where by the JVM and associated with the Throwable, but it is not visible to the debugger or reflection.

public static void main(String... args) {
    Throwable t = new Throwable("here");
    System.out.println("Throwable before getStackTrace()");
    shallowDump(t);

    System.out.println("\nThrowable after getStackTrace()");
    t.getStackTrace();
    shallowDump(t);
}

private static void shallowDump(Object pojo) {
    for (Field f : pojo.getClass().getDeclaredFields()) {
        if (Modifier.isStatic(f.getModifiers())) continue;
        f.setAccessible(true);
        Object o;
        try {
            o = f.get(pojo);
            if (o == pojo)
                o = "{self}";
            if (o instanceof Object[])
                o = "Array of "+(o.getClass().getComponentType());
        } catch (Exception e) {
            o = e;
        }
        System.out.println(f.getName() + ": " + o);
    }
}

prints

Throwable before getStackTrace()
detailMessage: here
cause: {self}
stackTrace: null

Throwable after getStackTrace()
detailMessage: here
cause: {self}
stackTrace: Array of class java.lang.StackTraceElement

So the question arises, where is the information which used to create the StackTraceElement retained. Looking at the code, native methods are used to access the information. There is a mysterious field called backtrace which you cannot see using reflection.

System.gc();
for (int i = 0; i < 5; i++) {
    Throwable[] ts = new Throwable[10000];
    long free = Runtime.getRuntime().freeMemory();
    for (int j = 0; j < ts.length; j++)
        ts[j] = new Throwable();
    long used = free - Runtime.getRuntime().freeMemory();
    System.out.printf("Average Throwable size was %,d%n", used / ts.length);
}
System.gc();
for (int i = 0; i < 5; i++) {
    Throwable[] ts = new Throwable[10000];
    long free = Runtime.getRuntime().freeMemory();
    for (int j = 0; j < ts.length; j++)
        ts[j] = Throwable.class.newInstance();
    long used = free - Runtime.getRuntime().freeMemory();
    System.out.printf("Average Throwable.class.newInstance() size was %,d%n", used / ts.length);
}

This gets the size of a Throwable created in the current method and a Throwable created in a deeper method via reflection (which has a deeper stack)

Average Throwable size was 302
Average Throwable size was 302
Average Throwable size was 302
Average Throwable size was 302
Average Throwable size was 302
Average Throwable.class.newInstance() size was 247
Average Throwable.class.newInstance() size was 296
Average Throwable.class.newInstance() size was 296
Average Throwable.class.newInstance() size was 296
Average Throwable.class.newInstance() size was 296

The size of a Throwable is much larger than you might expect from the fields it has. One can assume some additional information is being stored on the heap to help this class, however, is all the information were stored in the Throwable object , you expect the second type of Throwable to be larger.

like image 108
Peter Lawrey Avatar answered Oct 16 '22 00:10

Peter Lawrey