【发布时间】:2009-12-01 12:22:14
【问题描述】:
我了解 perm size 用于存储元数据,包括字节码、静态内容等。
我的问题是反射的使用如何影响烫发大小,如果有的话。 我的意思是,如果 Program-A 使用正常的运行对象方式,而 Program-B 全部使用反射,两个程序的 perm-size 将如何比较?
【问题讨论】:
标签: java reflection memory-management
我了解 perm size 用于存储元数据,包括字节码、静态内容等。
我的问题是反射的使用如何影响烫发大小,如果有的话。 我的意思是,如果 Program-A 使用正常的运行对象方式,而 Program-B 全部使用反射,两个程序的 perm-size 将如何比较?
【问题讨论】:
标签: java reflection memory-management
当您执行将加载新类或内部化字符串的代码时,永久空间将会增加。必须加载反射类,这是肯定的。我不确定反射 API 是否确实大量使用了内部化字符串,但应该不难发现。
例如对于方法getDeclaredMethod(String name, Class<?>... parameterTypes),名称参数将被内部化。
这个 sn-p 将很好地填满烫发空间:
Random random = new Random();
while(true){
try {
X.class.getDeclaredMethod(toBinaryString(random.nextLong()));
} catch (Exception e) {
}
}
alt text http://img4.imageshack.us/img4/9725/permgen.jpg
在现实世界的应用程序中没有任何区别。
【讨论】:
== 可以用于已经实习的方法名称。 (不相信这是个好主意。)从表面上看,反射可以创建类(有时)。
反射可以生成类,具体取决于实现。例如,您可能需要使用反射工件数千次才能将其编译为字节码。
与以往一样,建议是:在实际情况下分析您的代码。
【讨论】:
我不完全确定我理解了这个问题,但 Java 类已经包含了对它们执行反射所需的所有内容。 .class 文件不会根据其使用方式而改变。
编辑 1:提供一些更具体的示例以更好地区分我们正在谈论的“反射”类型。
例子:
class Foo {
public void bar() {
System.out.println( "Hello, world." );
}
}
// Conventional calling
Foo f = new Foo();
foo.bar();
// Reflection based callling
Class fooClass = Class.forName( "Foo" );
Method m = fooClass.getMethod( "bar", null );
Object f = fooClass.newInstance();
m.invoke( m, null );
在常规调用情况下,Foo.class 及其任何直接类依赖项将被加载。 bar 将被执行。字符串“Foo”和“bar”已经作为调用类的一部分被保留,因为字节码在运行时使用字符串来解析类和方法。 (实际上“bar”将是完整的方法签名,因此实际上比“bar”更长)
在反射的情况下,会发生完全相同的事情。唯一加载的附加类是 Method.class。这应该是对烫发大小的唯一影响。
后一种情况会影响性能。方法查找相对昂贵,因此建议尽可能缓存 Method 对象。调用的额外方法调用对性能有轻微影响,因为它是额外的方法调用。 Hotspot 将无法通过此调用进行优化……至少比正常情况要多。 JITing 的发生方式完全相同。
编辑 2:注意反射期间加载的一些其他对象...
java.lang.Class 将在访问时创建和缓存 Method 对象(或 Field 对象等)。这些缓存在 SoftReference 中,因此如果内存使用需要,它们将被回收。
然而,这些对象的初始化意味着虚拟机可能会加载额外的实习字符串来支持这些方法对象的创建。我的猜测是这些字符串可能已经是反射类常量池的一部分,但它们可能不是。无论哪种方式,每个类的每个方法,每个类的每个字段等都是一次命中。访问这些方法,您将至少获得这些方法的所有名称。访问这些字段,您将获得这些被实习的字段的名称。
【讨论】:
第一个答案基本上是正确的——一个类是“正常”加载还是通过反射加载没有区别。无论哪种方式,它都已加载。我想不出我所知道的实习字符串有什么不同。
我想有一个更间接的影响:使用反射,您可能会导致加载的类更少。假设您有一些代码基于某个参数加载 100 个类之一。无需反射地编写它,您将导入所有 100 个类并实例化其中一个。
使用反射,只会加载 1 个类。如果没有,在加载具有此代码的类时,将加载所有 100 个导入的类,因为所有类都被引用。因此,更多的类被加载到 perm 空间中。
但这是一个有些人为的例子。除非它确实描述了您的情况,否则我想您几乎不会注意到任何区别。也就是说,在您确定它不是微不足道之前,不要让这影响您的设计决策。
【讨论】: