How does reflection affect Perm size?

2019-04-06 07:48发布

问题:

I have the understanding that perm size is used to store meta data, that would include byte code, static stuff etc.

My question how does usage of reflection affect the perm size, if it ever does. I mean if Program-A uses normal way of running objects and Program-B uses reflection all over, how will the perm-sizes of the two programs compare?

回答1:

The perm space will grow when you execute code that will load new classes or internalize strings. The reflection classes have to be loaded that's for sure. I'm not sure if the reflection API does use internalized strings heavily but it should not be to hard to find out.

For example for the method getDeclaredMethod(String name, Class<?>... parameterTypes) the name parameter will be internalized.

This snippet will nicely fills up the perm space:

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

In a real world application it will make no difference.



回答2:

Reflection can generate classes, depending upon implementation. You may, for instance, need to use a reflection artifact thousands of times before it is compiled to bytecode.

As ever, the advice is: Profile your code in realistic situations.



回答3:

I'm not entirely sure I understand the question but Java classes already include everything need to perform reflection on them. A .class file doesn't change based on how it's used.

Edit 1: Providing some more specific examples to better distinguish the type of "reflection" we are talking about.

Example:

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 );     

In the conventional calling case, Foo.class and any of its direct class dependencies will be loaded. bar will be executed. The strings "Foo" and "bar" will already have been interned as part of the calling class because the byte code uses strings to resolve the classes and methods at runtime. (actually "bar" will be the full method signature so actually longer than just "bar")

In the reflection case, exactly the same thing happens. The only additional class loaded is Method.class. That should be the only affect to perm size.

The latter case has performance implications. The method look-up is relatively expensive so it's advisable to cache Method objects when you can. The extra method call to invoke has a slight performance implication as it's an extra method call. Hotspot will have trouble optimizing through this call... at least more than normal. JITing happens exactly the same.

Edit 2: Noting some additional objects that are loaded during reflection...

java.lang.Class will create and cache Method objects (or Field objects, etc.) upon access. These are cached in a SoftReference and so will be reclaimed if memory usage requires it.

However, the initialization of these objects means that there may be additional intern'ed strings loaded by the VM to support creation of these Method objects. My guess is that these strings were likely already part of the reflective class's constant pool but it's possible that they aren't. Either way, it's a single time hit per method per class, per field per class, etc.. Access the methods, you'll get at least all of the names for those methods intern'ed. Access the fields, you'll get the names of those fields intern'ed.



回答4:

The first answer is basically right -- whether a class is loaded 'normally' or via reflection makes no difference. Either way it's loaded. I can't think of any difference in interned Strings that I know of.

I suppose there is a more indirect effect: using reflection, you might cause fewer classes to be loaded. Let's say you have some code that loads one of 100 classes based on some parameter. Writing it without reflection, you'd import all 100 classes and instantiate one of them.

With reflection, only 1 class would be loaded. Without, at the time the class with this code loads, all 100 imported classes would load since all are referenced. So, more classes get loaded into the perm space.

But this is a somewhat contrived example. Unless it really described your situation, I imagine you will notice virtually no difference. That is, until you're sure it's not trivial, don't let this influence your design decisions.