Tuesday, April 1, 2008

Not out of memory


When I tried to build Sakai on a Solaris box today it failed. The only cryptic clue I received was this stacktrace

[exec] java.io.IOException: Cannot run program "env": error=12, Not enough space env=""
[exec] at java.lang.ProcessBuilder.start(ProcessBuilder.java:459)
[exec] at java.lang.Runtime.exec(Runtime.java:593)
[exec] at java.lang.Runtime.exec(Runtime.java:431)
[exec] at java.lang.Runtime.exec(Runtime.java:328)
[exec] at hidden.org.codehaus.plexus.util.cli.CommandLineUtils.getSystemEnvVars(CommandLineUtils.java:218)
[exec] at hidden.org.codehaus.plexus.util.cli.CommandLineUtils.getSystemEnvVars(CommandLineUtils.java:182)
[exec] at org.apache.maven.project.interpolation.RegexBasedModelInterpolator.<init>(RegexBasedModelInterpolator.java:63)
[exec] at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
[exec] at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
[exec] at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
[exec] at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
[exec] at java.lang.Class.newInstance0(Class.java:355)
[exec] at java.lang.Class.newInstance(Class.java:308)
[exec] at org.codehaus.plexus.component.factory.java.JavaComponentFactory.newInstance(JavaComponentFactory.java:44)
[exec] at org.codehaus.plexus.DefaultPlexusContainer.createComponentInstance(DefaultPlexusContainer.java:1464)
[exec] at org.codehaus.plexus.component.manager.AbstractComponentManager.createComponentInstance(AbstractComponentManager.java:93)
[exec] at org.codehaus.plexus.component.manager.ClassicSingletonComponentManager.getComponent(ClassicSingletonComponentManager.java:92)
[exec] at org.codehaus.plexus.DefaultPlexusContainer.lookup(DefaultPlexusContainer.java:331)
[exec] at org.codehaus.plexus.component.composition.FieldComponentComposer.assignRequirementToField(FieldComponentComposer.java:129)
[exec] at org.codehaus.plexus.component.composition.FieldComponentComposer.assembleComponent(FieldComponentComposer.java:73)
[exec] at org.codehaus.plexus.component.composition.DefaultComponentComposerManager.assembleComponent(DefaultComponentComposerManager.java:68)
[exec] at org.codehaus.plexus.DefaultPlexusContainer.composeComponent(DefaultPlexusContainer.java:1486)
[exec] at org.codehaus.plexus.personality.plexus.lifecycle.phase.CompositionPhase.execute(CompositionPhase.java:29)
[exec] at org.codehaus.plexus.lifecycle.AbstractLifecycleHandler.start(AbstractLifecycleHandler.java:101)
[exec] at org.codehaus.plexus.component.manager.AbstractComponentManager.startComponentLifecycle(AbstractComponentManager.java:105)
[exec] at org.codehaus.plexus.component.manager.AbstractComponentManager.createComponentInstance(AbstractComponentManager.java:95)
[exec] at org.codehaus.plexus.component.manager.ClassicSingletonComponentManager.getComponent(ClassicSingletonComponentManager.java:92)
[exec] at org.codehaus.plexus.DefaultPlexusContainer.lookup(DefaultPlexusContainer.java:331)
[exec] at org.codehaus.plexus.component.composition.FieldComponentComposer.assignRequirementToField(FieldComponentComposer.java:129)
[exec] at org.codehaus.plexus.component.composition.FieldComponentComposer.assembleComponent(FieldComponentComposer.java:73)
[exec] at org.codehaus.plexus.component.composition.DefaultComponentComposerManager.assembleComponent(DefaultComponentComposerManager.java:68)
[exec] at org.codehaus.plexus.DefaultPlexusContainer.composeComponent(DefaultPlexusContainer.java:1486)
[exec] at org.codehaus.plexus.personality.plexus.lifecycle.phase.CompositionPhase.execute(CompositionPhase.java:29)
[exec] at org.codehaus.plexus.lifecycle.AbstractLifecycleHandler.start(AbstractLifecycleHandler.java:101)
[exec] at org.codehaus.plexus.component.manager.AbstractComponentManager.startComponentLifecycle(AbstractComponentManager.java:105)
[exec] at org.codehaus.plexus.component.manager.AbstractComponentManager.createComponentInstance(AbstractComponentManager.java:95)
[exec] at org.codehaus.plexus.component.manager.ClassicSingletonComponentManager.getComponent(ClassicSingletonComponentManager.java:92)
[exec] at org.codehaus.plexus.DefaultPlexusContainer.lookup(DefaultPlexusContainer.java:331)
[exec] at org.codehaus.plexus.embed.Embedder.lookup(Embedder.java:78)
[exec] at org.apache.maven.cli.MavenCli.createMavenInstance(MavenCli.java:474)
[exec] at org.apache.maven.cli.MavenCli.main(MavenCli.java:257)
[exec] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[exec] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
[exec] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
[exec] at java.lang.reflect.Method.invoke(Method.java:597)
[exec] at org.codehaus.classworlds.Launcher.launchEnhanced(Launcher.java:315)
[exec] at org.codehaus.classworlds.Launcher.launch(Launcher.java:255)
[exec] at org.codehaus.classworlds.Launcher.mainWithExitCode(Launcher.java:430)
[exec] at org.codehaus.classworlds.Launcher.main(Launcher.java:375)
[exec] Caused by: java.io.IOException: error=12, Not enough space
[exec] at java.lang.UNIXProcess.forkAndExec(Native Method)
[exec] at java.lang.UNIXProcess.<init>(UNIXProcess.java:53)
[exec] at java.lang.ProcessImpl.start(ProcessImpl.java:65)
[exec] at java.lang.ProcessBuilder.start(ProcessBuilder.java:452)
[exec] ... 48 more

After digging around it and trying different ulimit settings it turned out to be that the /tmp partition was too full.

Friday, February 29, 2008

Every Class

This week I wanted to find out which classes are loaded in running environment.

One way of achieving this is replacing the boot classloader and finding a way to keep a reference to every loaded class by every child classloader. This sounded like too much work and I find it very scary to replace system classes with my own which probably don't have the same quality.

Another approach is to write a javaagent. This gives access to the java.lang.instrument.Instrumentation class which in turn has the getAllLoadedClasses() method. Exactly the information I was after and without many nasty side effects or jumping through burning hoops.

Now that I found this information I needed a way to publish it. A colleague of mine pointed me in the direction of JMX. Using JConsole to view which classes are loaded sound great to me so after a little hacking I came up with a javaagent which registers itself as an MBean.

Here's the code:

MANIFEST.MF

Manifest-Version: 1.0
Premain-Class: somecoder.classes.Classes


ClassesMBean.java

package somecoder.classes;

public interface ClassesMBean {
String[] getClasses();
long getClassCount();
}


Classes.java

package somecoder.classes;

import java.lang.instrument.Instrumentation;
import java.lang.management.ManagementFactory;

import javax.management.ObjectName;

public final class Classes implements ClassesMBean {
private Instrumentation inst;

private Classes(final Instrumentation inst) {
this.inst = inst;
}

public static void premain(final String agentArgs,
final Instrumentation inst) {
try {
final Classes classLister = new Classes(inst);
ManagementFactory.getPlatformMBeanServer().registerMBean(classLister, new ObjectName("Classes:type=Agent"));
} catch (Exception e) {
e.printStackTrace();
}
}

public long getClassCount() {
return inst.getAllLoadedClasses().length;
}

public String[] getClasses() {
final Class[] classes = inst.getAllLoadedClasses();
final String[] strings = new String[classes.length];
for (int i = 0; i < classes.length; i++) {
strings[i] = classes[i].getName();
}
return strings;
}
}