Java-Gaming.org    
Featured games (79)
games approved by the League of Dukes
Games in Showcase (477)
Games in Android Showcase (108)
games submitted by our members
Games in WIP (536)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  ClassLocater...only, it's bugged  (Read 2700 times)
0 Members and 1 Guest are viewing this topic.
Offline blahblahblahh

JGO Coder


Medals: 1


http://t-machine.org


« Posted 2004-11-05 18:33:52 »

Based on duncanIdaho's ClassPath class, I modded this code. Aim is to "find all classes that are instances of a given interface or class" at runtime. I'd been using it OK for a while, and adding small fixes as problems came up (e.g. additional Throwable's).

But now...I'm getting a massive memory leak, and no idea how to solve it. Mem usage goes from 64MB to over 160MB before process is killed - just from this one method.

Note that I ran this on the same PC 12 hours earlier wihtout problems, and it produce approx 4 classes as a result of the method (which is correct - there are only 4 test instances for the code I'm currently working with).

So...AFAICS there is no reason why this shouldn't at the very least GC if it spirals out of control; I've checked that it (theoretically) isn't hanging onto references.

EDIT: see the rest of thread for details, but basically the OOME is from too much method calling, hence sidesteps GC. Silly me can no longer recognise an infinite recusive loop in java  runtimes (probably simply because it looks different in logging frameworks compared to in DOS prompt where I learnt it Smiley)

I could try to limit by package name using the regex, but I'd like to be fully automatic and not rely on unreliable voluntary naming conventions! Meanwhile, any ideas on why this code OutOfMemoryError's on 1.4.2_05 instead of GC'ing?

NB: ignore the poor package name; that was from before i got the JGF domain - it will be fixed before I post the JAR!

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
package com.grexengine.jgf;

import java.util.ArrayList;
import java.util.Iterator;

/**
 * An essential part of Java - locates any Class, anywhere.
 * </P>
 * This feature would have been nice as part of the JDK for the last 7 years, but Sun hasn't
 * added it, so we did it instead.
 * <P>
 * Browse the different static methods to see what kinds of search you can do...
 */

public class ClassLocater
{
      /**
      * Find all instances of the given <code>Class</code> or interface by
      * loading all classes on the class path.
      *  
      * @param targetType the superclass of all returned classes.
      * @return an array of all subclasses of <code>targetType</code>
      */

      public static Class[] getSubclassesOf( Class targetType )
      {
            return getSubclassesOf( targetType, ".*" );
      }
                              
      /**
      * Find all subclasses of the given <code>Class</code> or interface by
      * loading only those classes with names that match the given regular
      * expression.
      *  
      * @param targetType the superclass of all returned classes.
      * @param regex a regular expression that will match with every subclass
      * @return an array of all subclasses of <code>targetType</code>
      */

      public static Class[] getSubclassesOf( Class targetType, String regex )
      {
            ArrayList matches = new ArrayList();
            ClassPath cp = new ClassPath();
            Iterator i = cp.classNameIterator();
            while ( i.hasNext() )
            {
                  String className = (String)i.next();
                  if ( className.matches( regex )
                  && !className.equals( targetType.getName() ) )
                  {
                        Class clazz = null;
                        try
                        {
                              clazz = Class.forName( className );
                        }
                        catch (ClassNotFoundException cnfx )
                        {
                              continue;
                              
                        }
                        catch (NoClassDefFoundError cnfx )
                        {
                              continue;
                              
                        }
                        catch( UnsatisfiedLinkError cnfx )
                        {
                              continue;
                        }
                        finally
                        {
                              if ( clazz != null && targetType.isAssignableFrom( clazz ) )
                              {
                                    matches.add( clazz );
                              }
                        }  
                              
                  }
                        
            }
                  
            return (Class[])matches.toArray( new Class[0] );
            
      }
}

malloc will be first against the wall when the revolution comes...
Offline kevglass

JGO Kernel


Medals: 122
Projects: 23
Exp: 18 years


Coder, Trainee Pixel Artist, Game Reviewer


« Reply #1 - Posted 2004-11-06 07:55:12 »

Are you under the impression that this exact same piece of code worked fine earlier, or have their been "minor mods" since then?

Can't see anything wrong with the code you've got there but then it'd be nice to see "class ClassPath" aswell.

Kev

Offline blahblahblahh

JGO Coder


Medals: 1


http://t-machine.org


« Reply #2 - Posted 2004-11-06 08:30:28 »

Quote
Are you under the impression that this exact same piece of code worked fine earlier, or have their been "minor mods" since then?

Can't see anything wrong with the code you've got there but then it'd be nice to see "class ClassPath" aswell.

Kev


No mods for a long while. Interestingly, with a regex of "com.grexengine.jgf..*" it still OOME's - though I would ahve thought that would avoid most of the allocations.

malloc will be first against the wall when the revolution comes...
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline blahblahblahh

JGO Coder


Medals: 1


http://t-machine.org


« Reply #3 - Posted 2004-11-06 08:31:57 »

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
216  
217  
218  
219  
220  
221  
222  
223  
224  
225  
226  
227  
228  
229  
230  
231  
232  
233  
234  
235  
236  
237  
238  
239  
240  
241  
242  
243  
244  
245  
246  
247  
248  
249  
250  
251  
252  
253  
254  
255  
256  
257  
258  
259  
260  
261  
262  
263  
264  
265  
266  
267  
268  
269  
270  
271  
272  
273  
274  
275  
276  
277  
278  
279  
280  
281  
282  
283  
284  
285  
286  
287  
288  
289  
290  
291  
292  
293  
294  
295  
296  
297  
298  
299  
300  
301  
302  
303  
304  
305  
306  
307  
308  
309  
310  
311  
312  
313  
314  
315  
316  
317  
318  
319  
320  
321  
322  
323  
324  
325  
326  
327  
328  
329  
330  
331  
332  
333  
334  
335  
336  
337  
338  
339  
340  
341  
package com.grexengine.jgf;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
 
 
/**
 * Created with Eclipse  
 * User: duncanIdaho for java-gaming.org
 *
 */

 
/**
 * ClassPath find and records the fully qualified name of every Class  
 * on the classpath via the system property "java.class.path".
 *  
 */

public class ClassPath {
 
 public static final int SILENT = 0;
 public static final int QUIET = 1;
 public static final int VERBOSE = 2;
 
 public static final String JAR_EXT = ".jar";
 public static final String ZIP_EXT = ".zip";
 public static final String CLASS_EXT = ".class";
 
 private ArrayList classNames;
 
 private int outputLevel;
 
 /**
  * create a new ClassPath instance and find all classes on the classpath
  *
  */

 public ClassPath() {
  this( SILENT );
   
 }
 
 
 public ClassPath(int type) {
  super();
  outputLevel = type;
  findAllClassNames();
 
 }
 
 /**
  * Answers an <code>Iterator</code> over fully qualified <code>Class</code>
  * names found on the classpath.
  * @return an <code>Iterator</code> over the elements in this list
  */

 public Iterator classNameIterator() {
  return classNames.iterator();
   
 }
 
 /**
  * Answers an <code>ArrayList</code> of all <code>Class</code> names found
  * on the classpath.
  * @return an <code>ArrayList</code> of class names.
  */

 public ArrayList getClassNames() {
  return classNames;
   
 }
 
 /**
  * Initialize the member variable <code>classNames</code> and  
  * look for classes.
  *
  */

 private void findAllClassNames() {
  String path = null;
  classNames = new ArrayList();
  try {
   path = System.getProperty( "java.class.path" );
  } catch ( Exception x ) {
   x.printStackTrace();
   
  }
  if ( outputLevel != SILENT )
   System.out.println( "scanning classpath: " + path );
  StringTokenizer toke = new StringTokenizer( path, File.pathSeparator );
   
  analyzeClasspathTokens( toke );
 
  if ( outputLevel != SILENT )
   System.out.println( "found " + classNames.size() + " classes." );
   
  if ( outputLevel == VERBOSE ) {
   Iterator i = classNames.iterator();
   while ( i.hasNext() ) {
    String name = (String)i.next();
    System.out.println( name );
     
   }
   
  }
   
 }
 
 /**
  * Adds a file explicitly mentioned on the classpath to the list  
  * of classes.
  * @param classFile a class file listed on the classpath itself.
  */

 private void addClass( File classFile ) {
  classNames.add( getClassNameFrom( classFile.getName() ) );
   
 }
 
 /**
  * Adds all class names found in the jar.
  * @param jarFile a jar file explicitly listed on the classpath.
  */

 private void addJarContents( File jarFile ) {
  JarFile jar = null;
  try {
   jar = new JarFile( jarFile );
 
  } catch ( IOException iox ) {
   // boom!
 }
  if ( jar != null ) {
   Manifest man = null;
   try {  
    man = jar.getManifest();
   } catch( IOException iox ) {
    System.err.println("error obtaining manifest from: " + jar.getName());
     
   } finally {
    if ( man != null ) {
     this.scanClasspath( man, jar, jarFile );
     
    }
     
   }
   Enumeration e = jar.entries();
   while (e.hasMoreElements()) {
    JarEntry entry = (JarEntry)e.nextElement();
    if ( !entry.isDirectory() && entry.getName().endsWith( CLASS_EXT ) ) {
     String className = getClassNameFrom( entry.getName() );
     classNames.add( className );
 
    }
   
   }  
 
  }
   
 }
 
 /**
  * Adds all class names found in the zip mentioned  
  * @param zipFile
  */

 private void addZipContents( File zipFile ) {
  ZipFile zip = null;
  try {
   zip = new JarFile( zipFile );
   
  } catch ( IOException iox ) {
   
  }
  if ( zip != null ) {
   Enumeration e = zip.entries();
   while (e.hasMoreElements()) {
    ZipEntry entry = (ZipEntry)e.nextElement();
    if ( !entry.isDirectory() && entry.getName().endsWith( CLASS_EXT ) ) {
     String className = getClassNameFrom( entry.getName() );
     classNames.add( className );
     
    }
   }  
  }  
 }
 
 /**
  * This method takes a top level classpath dir i.e. 'classes' or bin
  * @param dir
  */

 private void addDirectoryContents( File dir ) {
  // drill through contained dirs ... this is expected to be the  
 // 'classes' or 'bin' dir
 File files[] = dir.listFiles();
  for( int i = 0; i < files.length ; ++i ) {
   File f = files[i];
   if ( f.isDirectory() ) {
    addDirectoryContents( "", f );
     
   } else {
    if ( f.getName().endsWith( CLASS_EXT ) )
     addClass( f );
       
   }
   
  }
   
 }
 
 /**
  * This method does the real directory recursion, passing along the  
  * the corresponding package-path to this directory.
  *  
  * @param pathTo the preceding path to this directory
  * @param dir a directory to search for class files
  */

 private void addDirectoryContents( String pathTo, File dir ) {
  String pathToHere = pathTo + dir.getName() + File.separator;
  File files[] = dir.listFiles();
  for( int i = 0; i < files.length ; ++i ) {
   File f = files[i];
   if ( f.isDirectory() ) {
    addDirectoryContents( pathToHere, f );
     
   } else {
    if ( f.getName().endsWith( CLASS_EXT ) ) {
     String absFilePath = pathToHere + f.getName();
     classNames.add( getClassNameFrom( absFilePath ) );
     
    }
       
   }
   
  }
   
 }
 
 /**
  * While the StringTokenizer has classpath elements, attempt to  
  * add any contained classes to the list.
  *  
  * @param toke class path elements
  */

 private void analyzeClasspathTokens( StringTokenizer toke ) {
  while ( toke.hasMoreTokens()) {
   String pathElement = toke.nextToken();
   analyzeClasspathElement( pathElement );
 
  }
   
 }
 
 /**
  * Make a file out of the String, determine which kind of  
  * interesting classpath file it might be, and add it to the list.
  *  
  * @param pathElement  
  */

 private void analyzeClasspathElement( String pathElement ) {
  File elementFile = new File( pathElement );
  String elementName = elementFile.getAbsolutePath();
  if ( elementName.endsWith( JAR_EXT ) ) {
   addJarContents( elementFile );
   
  } else if ( elementName.endsWith( ZIP_EXT ) ) {
   addZipContents( elementFile );
   
  } else if ( elementName.endsWith( CLASS_EXT ) ) {
   addClass( elementFile );
   
  } else {
   addDirectoryContents( elementFile );
   
  }
   
 }
 
 /**
  * replace ANY slashes with dots and remove the .class at the  
  * end of the file name.
  * @param entryName a file name relative to the classpath.  A class  
  * of package org found in directory bin would be passed into this  
  * method as "org/MyClass.class"
  * @return a fully qualified Class name.
  */

 private String getClassNameFrom( String entryName ) {
  String foo = new String(entryName).replace( '/', '.' );
  foo = foo.replace( '\\', '.' );
  return foo.substring( 0, foo.lastIndexOf( '.' ) );
   
 }
 
 /**
  * Use the manifest associated with the jar to determine if there are
  * any Class-Path elements names in the jar that should also be scanned.
  *  
  * @param man the manifest of the given jar
  * @param jar the jar associated with the given manifest.
  */

 private void scanClasspath( Manifest man, JarFile jar, File jarFile ) {
  Map map = man.getEntries();
  if ( map != null ) {
   Attributes atts = man.getMainAttributes();
   if ( atts != null ) {
    Set keys = atts.keySet();
    Iterator i = keys.iterator();
    while ( i.hasNext() ) {
     Object key = (Object)i.next();
     String value = (String)atts.get( key );
     if ( outputLevel == VERBOSE )
      System.out.println( jar.getName() + "  " + key + ": " + value );
     if ( key.toString().equals( "Class-Path" )) {
      if ( outputLevel != SILENT )
       System.out.println( "scanning " + jar.getName() +"'s manifest classpath: " + value);
      StringTokenizer toke = new StringTokenizer( value );
      while (toke.hasMoreTokens() ) {
       String element = toke.nextToken();
       if ( jarFile.getParent() == null )
        analyzeClasspathElement( element );
       else {
        analyzeClasspathElement( jarFile.getParent() + File.separator +  element );
       }
       
      }
       
     }
     
    }
     
   }  
   
  }
   
 }
 
}

malloc will be first against the wall when the revolution comes...
Offline kevglass

JGO Kernel


Medals: 122
Projects: 23
Exp: 18 years


Coder, Trainee Pixel Artist, Game Reviewer


« Reply #4 - Posted 2004-11-06 08:58:42 »

Teach granny to suck eggs here I suppose, but does it get past constructing the ClassPath object?

Kev

Online Spasi
« Reply #5 - Posted 2004-11-06 09:03:49 »

IIRC Class.forName() not only loads a class but also makes any static initialization to occur (execution of static blocks and static fields). Maybe a new class has been added to the system that creates some huge static objects?
Offline blahblahblahh

JGO Coder


Medals: 1


http://t-machine.org


« Reply #6 - Posted 2004-11-06 10:45:23 »

Quote
Teach granny to suck eggs here I suppose, but does it get past constructing the ClassPath object?

Kev


I thought so, but ... I'll check anyway. Heck, it needs some logging statements in there anyway.

malloc will be first against the wall when the revolution comes...
Offline blahblahblahh

JGO Coder


Medals: 1


http://t-machine.org


« Reply #7 - Posted 2004-11-06 10:46:32 »

Quote
IIRC Class.forName() not only loads a class but also makes any static initialization to occur (execution of static blocks and static fields). Maybe a new class has been added to the system that creates some huge static objects?


Doh. Of course, that could explain it...however, I don't believe I have added anything with static inits of non trivial size.

I'll go do some more logging and come back with more data. I'd been hoping I was missing something obvious, but obviously not Sad.

malloc will be first against the wall when the revolution comes...
Offline blahblahblahh

JGO Coder


Medals: 1


http://t-machine.org


« Reply #8 - Posted 2004-11-06 12:30:59 »

Oops. Seen the problem - didaho's algo only works on toy examples (no offense, but just think what happens when you get mutually referential manifests GULP).

Rewriting it now to be iterative rather than ultra-deep (potentially infinite) recursive. I think it was collapsing with OOME because of too much recursion, 99.99% of which was unnecessary Sad.

EDIT: just to  be clear, DI never presented his code as perfect, so it's fair enough that it's not optimized or anything. I adopted it in order to polish it a bit. It's just that it had worked perfectly for lots of small situations and I'd got used to it doing so, without bothering to go through the code with a fine tooth comb.

malloc will be first against the wall when the revolution comes...
Offline blahblahblahh

JGO Coder


Medals: 1


http://t-machine.org


« Reply #9 - Posted 2004-11-06 14:05:27 »

OK, I rewrote the ClassPath from scratch and now it doesn't fail even in mutually (infinitely) recursive situations, and it's noticeably faster in most cases. Grin.

If someone wants the code urgently, email me (ceo @ grexengine.com) and I'll give you a fixed-up binary JAR. Otherwise, it can wait until JGFv3 is up, and will appear as one of the first 5 items in the "source code" section.

Ironically I was using these classes to auto-discover JGF-specific modules during the JGF server's boot process when I discovered this problem Grin.

malloc will be first against the wall when the revolution comes...
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

CogWheelz (16 views)
2014-07-30 21:08:39

Riven (22 views)
2014-07-29 18:09:19

Riven (14 views)
2014-07-29 18:08:52

Dwinin (12 views)
2014-07-29 10:59:34

E.R. Fleming (32 views)
2014-07-29 03:07:13

E.R. Fleming (12 views)
2014-07-29 03:06:25

pw (42 views)
2014-07-24 01:59:36

Riven (42 views)
2014-07-23 21:16:32

Riven (30 views)
2014-07-23 21:07:15

Riven (31 views)
2014-07-23 20:56:16
HotSpot Options
by dleskov
2014-07-08 03:59:08

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:58:24

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:47:22

How do I start Java Game Development?
by ra4king
2014-05-17 11:13:37

HotSpot Options
by Roquen
2014-05-15 09:59:54

HotSpot Options
by Roquen
2014-05-06 15:03:10

Escape Analysis
by Roquen
2014-04-29 22:16:43

Experimental Toys
by Roquen
2014-04-28 13:24:22
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!