Limiting JVM on VPS (Virtual Private Server)
What Are Virtual Private Servers?
Virtual Private Servers are the most advanced and affordable solution for anyone wanting to run their own server. They are used to partition a single physical server into many isolated virtual private servers. Each virtual private server looks and behaves exactly like a real networked server system, complete with its own set of init scripts, users, processes, and file systems. There is one downside to VPS: running Java on it. JVM only sees the computer as it is, 16Gigabytes of RAM and if you are attempting to start up an Application Server, JVM will happily attempt to reserve, say, 2 gigabytes and will not be able to do so in a 512 megabyte account. How can you get around this?
VPS is definitely an affordable choice of running a J2EE server or even a Java Container such as Tomcat (for those that have less than 512 Mb). Unlike the Private Server where you are the master of domain and all the CPU power as well as all of the RAM available belongs to you, VPS shares the CPU, RAM, and other space with other users (although not as many as on Shared Hosting).
How do you solve the problem of using JVM on a VPS?
In most cases, be that a J2EE App. server like JBoss or something else, if you do not explicitly tell the JVM to limit its consumption of RAM, JVM will attempt to gather a default (if available, for that product) value and use thatt as its limit. For example, Sun's open source Application Server Glassfish has a default requirement of 512 Mb of ram and would throw the following:
[blah@blah]# java -jar glass*jar -console Exception in thread "main" java.lang.OutOfMemoryError at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:620) at java.security.SecureClassLoader.defineClass (SecureClassLoader.java:124) at java.net.URLClassLoader.defineClass (URLClassLoader.java:260) at java.net.URLClassLoader.access$100(URLClassLoader.java:56) at java.net.URLClassLoader$1.run(URLClassLoader.java:195) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:188) at java.lang.ClassLoader.loadClass(ClassLoader.java:306) at sun.misc.Launcher$AppClassLoader.loadClass (Launcher.java:268) at java.lang.ClassLoader.loadClass(ClassLoader.java:251) at java.lang.ClassLoader.loadClassInternal (ClassLoader.java:319)
Even if you specify java -Xmx256m -jar glass*jar -console, JVM will not obey to run all involved JARs with the parameter -Xmx256m. In fact, in this case you are limited to 256 Mb of ram to just running that JAR.
As suggested, if you have over 512 Mb of RAM, you will have no problem installing Glassfish on a VPS server: java -Xmx512m -jar glass*jar -console.
Another type of exception may look like this:
Exception in thread "main" java.io.IOException: java.io.IOException: Cannot allocate memory at java.lang.UNIXProcess.<init>(UNIXProcess.java:148) at java.lang.ProcessImpl.start(ProcessImpl.java:65) at java.lang.ProcessBuilder.start(ProcessBuilder.java:451) at java.lang.Runtime.exec(Runtime.java:591) at java.lang.Runtime.exec(Runtime.java:464)
The above exception is applicable to *Nix-based operating systems.
Another quite interesting behavior is that JVM may be able to run whatever it is you want but might not be capable of deallocating the resource you took and you will receive this:
Java HotSpot(TM) Client VM warning: Attempt to deallocate stack guard pages failed. Java HotSpot(TM) Client VM warning: Attempt to allocate stack guard pages failed.
The solutions that I found through numerous attempts and failures are the following:
1. Limit the RAM for JBoss Application Server
Locate the runnable script RUN.SH in the $JBOSS_HOME/bin directory. Edit this file and explicitly add -Xmx and/or -Xms.
Please remember that, even if you have 512 MB of RAM on your VPS, you will not be able to specify -Xmx500m and expect it to use just 500 megs of RAM.
Please adjust accordingly and start from -Xmx32m. You will see that your JBoss will be able to start even if you have just 128 megs of RAM on your account.
2. Use wide settings because it's a local variable
Tested on RedHat Fedora Linux, I have been able to force the JVM to pick up the limits like this:
_JAVA_OPTIONS='-Xmx128m' export _JAVA_OPTIONS
This special variable that MUST have an underscore in front will force the JVM to obey the limits and run whatever you want. Beware, however, that this limit is global for the user and all calls: java -version will always use these EXPLICIT options.
3. A tricky way
You also could create an executable script on your *NIX account and in it call the Java command with appropriate options. The content of the file may be something like this:
#!/bin/sh exec /usr/java/jdk1.6.0/bin/java -Xmx64m "$@"