【问题标题】:How does reflection affect Perm size?反射如何影响烫发大小?
【发布时间】:2009-12-01 12:22:14
【问题描述】:

我了解 perm size 用于存储元数据,包括字节码、静态内容等。

我的问题是反射的使用如何影响烫发大小,如果有的话。 我的意思是,如果 Program-A 使用正常的运行对象方式,而 Program-B 全部使用反射,两个程序的 perm-size 将如何比较?

【问题讨论】:

    标签: java reflection memory-management


    【解决方案1】:

    当您执行将加载新类内部化字符串的代码时,永久空间将会增加。必须加载反射类,这是肯定的。我不确定反射 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

    在现实世界的应用程序中没有任何区别。

    【讨论】:

    • 我相信您看到的是对搜索的名称进行实习,以便== 可以用于已经实习的方法名称。 (不相信这是个好主意。)从表面上看,反射可以创建类(有时)。
    • (那些实习字符串是 GCable 且具有完整的垃圾收集周期,假设实现质量良好。)
    • @Tom,您能否提供一个示例,其中由于反射而创建了一个类?我不是想成为一个痛苦的人,我真的很想知道这些我从未见过的案例。
    • @PSpeed-@Tom...我不确定,但我们正在讨论这一点,有人说如果你每次都创建一个新的 JAXBContext,那么 JaxB 似乎每次都会创建新的类。这里有一个错误:jaxb.dev.java.net/issues/show_bug.cgi?id=581
    • @PSpeed..为什么你认为给定的 sn-p 会填满烫发空间。还附上给定 sn-p 的配置文件的图片!
    【解决方案2】:

    反射可以生成类,具体取决于实现。例如,您可能需要使用反射工件数千次才能将其编译为字节码。

    与以往一样,建议是:在实际情况下分析您的代码。

    【讨论】:

    • 你的意思是上面的“本机代码”吗?这些类已经是字节码。 Hotspot 将无法通过反射调用进行优化,但 JIT 的行为相同。
    • 不,我是说字节码。显然使用 HotSpot 后会将字节码编译为原生。可以通过查找距对象开头的偏移量并添加来读取字段。也可以通过编写读取这些字段的字节码(忽略访问权限)来读取它们。类似的事情还在继续(在 Sun JRE 中),但我忘记了细节。
    • +1:只需激活 GC 日志并检查 System.out - [Unloading class sun.reflect.GeneratedMethodAccessor1403] [Unloading class sun.reflect.GeneratedConstructorAccessor446] [Unloading class sun.reflect.GeneratedMethodAccessor562] - 这些由反射实现生成。
    【解决方案3】:

    我不完全确定我理解了这个问题,但 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 中,因此如果内存使用需要,它们将被回收。

    然而,这些对象的初始化意味着虚拟机可能会加载额外的实习字符串来支持这些方法对象的创建。我的猜测是这些字符串可能已经是反射类常量池的一部分,但它们可能不是。无论哪种方式,每个类的每个方法,每个类的每个字段等都是一次命中。访问这些方法,您将至少获得这些方法的所有名称。访问这些字段,您将获得这些被实习的字段的名称。

    【讨论】:

    • 类可以由反射实现动态生成,以便快速访问。
    • 请参考?我一直使用反射(真的......一直)并且从未见过因此生成的单个类。第三方库可能会做一些事情,动态代理的东西肯定会生成一个运行时类。但不应该简单地调用类/对象上的方法。
    • @PSpeed..请检查以下链接jaxb.dev.java.net/issues/show_bug.cgi?id=581
    • @Tom...你能举个例子吗...这正是我想要的!
    【解决方案4】:

    第一个答案基本上是正确的——一个类是“正常”加载还是通过反射加载没有区别。无论哪种方式,它都已加载。我想不出我所知道的实习字符串有什么不同。

    我想有一个更间接的影响:使用反射,您可能会导致加载的类更少。假设您有一些代码基于某个参数加载 100 个类之一。无需反射地编写它,您将导入所有 100 个类并实例化其中一个。

    使用反射,只会加载 1 个类。如果没有,在加载具有此代码的类时,将加载所有 100 个导入的类,因为所有类都被引用。因此,更多的类被加载到 perm 空间中。

    但这是一个有些人为的例子。除非它确实描述了您的情况,否则我想您几乎不会注意到任何区别。也就是说,在您确定它不是微不足道之前,不要让这影响您的设计决策。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-25
      • 2016-07-19
      • 2014-06-19
      • 2017-10-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多