Java 异常的内存分配
您能解释一下,异常实例或它的子实例在内存中的分配位置吗?是堆还是栈,还是别的什么Java 异常的内存分配,java,memory,garbage-collection,Java,Memory,Garbage Collection,您能解释一下,异常实例或它的子实例在内存中的分配位置吗?是堆还是栈,还是别的什么 谢谢 默认情况下,Java中的所有对象都分配在一个堆中。您可以特别指出,关于异常实例,因为它们通常被传递给调用方方法,因此它们不可能位于堆栈上。异常s(以及所有可丢弃的s)与任何其他类型的Java对象一样。因此,它们将出现在堆中。正如MusiKk指出的那样,堆栈只能保存原语值或对象引用。它们是在堆上创建的,但这有什么关系呢?因为大多数JVM的所有对象都是在堆上创建的,异常不是异常。) JVM可以使用转义分析在堆栈上
谢谢 默认情况下,Java中的所有对象都分配在一个堆中。您可以特别指出,关于异常实例,因为它们通常被传递给调用方方法,因此它们不可能位于堆栈上。
异常
s(以及所有可丢弃的s)与任何其他类型的Java对象一样。因此,它们将出现在堆中。正如MusiKk指出的那样,堆栈只能保存原语值或对象引用。它们是在堆上创建的,但这有什么关系呢?因为大多数JVM的所有对象都是在堆上创建的,异常不是异常。)
JVM可以使用转义分析在堆栈上分配对象,但是这通常限于仅在一个方法中使用且未返回的对象。i、 e.例外情况极不可能是一个好的候选情况
在许多JVM上创建可丢弃文件(包括异常)的方式有一个特殊之处,即堆栈跟踪元素在需要时才创建。这是因为大多数时候它们是不需要的,而且它们的创建成本很高。但是,用于创建堆栈跟踪的信息被JVM保留在某个位置,并与可丢弃文件关联,但调试器或反射不可见
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);
}
}
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);
}
印刷品
Throwable before getStackTrace()
detailMessage: here
cause: {self}
stackTrace: null
Throwable after getStackTrace()
detailMessage: here
cause: {self}
stackTrace: Array of class java.lang.StackTraceElement
因此问题出现了,用于创建StackTraceElement的信息保留在哪里。查看代码,本机方法用于访问信息。有一个神秘的字段叫做
backtrace
,使用反射无法看到它
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);
}
}
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);
}
一个可丢弃文件的大小比你从它的字段中所期望的要大得多。我们可以假设堆中存储了一些额外的信息来帮助这个类,但是,如果所有信息都存储在Throwable对象中,那么您希望第二种Throwable对象更大。对象总是在堆中。堆栈上只能有引用。在99.9%的情况下,是的。。。但是,最新的JDK可以使用Escape分析来确定对对象的引用是否超出了当前范围,如果没有,它根本不会创建对象,如果空间允许,它会将基本字段直接放入堆栈中。再说一次,
Throwable
的字段也是对象,因此它们无论如何都必须放在堆上,所以我想我并不是一直都在思考问题。如果JVM想要符合VM规范,它就不能在堆栈上创建对象。堆栈只能保存引用。请参阅JVM的3.6.1:@musiKk,我同意编译器不能为虚拟机分配虚拟堆栈上的对象。然而,JVM编译为本机代码时所做的工作可能非常不同。我也在考虑转义分析,但由于Throwable
的字段也是对象,因此可能无法工作。请看我的答案。JVM而不是编译器执行转义分析。因此,虚拟机的字节码是什么并不重要。