Using the Client JVM and controlling RAM when building a Scala/Java/Mac application

For my TypewriterFX application (which plays Mac OS X typewriter sound effects), I wanted/needed to control the JVM that was used to run the application. The way it works, TypewriterFX plays sounds as fast as it can — as fast as you type — so I needed the JVM to be more like the old client JVM than a typical server JVM.

I use Ant and the Java 7 AppBundlerTask to build TypewriterFX as a Mac OS X application, and as part of that build process I set some JVM properties. Without looking at the entire build.xml file, I’ll show you that these are the JVM properties I set to control the JVM RAM usage, and to also use a “client” JVM for my app:

<!-- jvm settings -->
<option value="-Xms32m"/>
<option value="-Xmx128m"/>
<option value="-XX:PermSize=20m"/>
<option value="-XX:MaxPermSize=20m"/>

<!-- per oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html -->
<!-- "use cms for desktop ui" -->
<option value="-XX:+UseConcMarkSweepGC"/>
<option value="-XX:ParallelCMSThreads=2"/>

If you dig through that Oracle URL you’ll find why I’m using these properties, but in short, as the comment shows, they suggest using the CMS JVM for desktop applications.

The complete Ant build.xml file

If you’re interested in the complete Ant build.xml file I use to build my Java application as a Mac OS X application ... drumroll ... here it is:

<project name="TypewriterFX" default="create-bundle" basedir=".">

    <!-- SYSTEM DEPENDENT -->
    <property name="scalaLibrary" value="/Users/al/bin/scala-2.10.3/lib/scala-library.jar" />

    <!-- assumes appbundler-1.0.jar is in the current dir -->
    <taskdef name="bundleapp" classname="com.oracle.appbundler.AppBundlerTask" classpath="appbundler-1.0.jar" />
    <property environment="env" />

    <!-- ProGuard -->
    <taskdef resource="proguard/ant/task.properties" classpath="./proguard.jar" />

    <!-- input stuff -->
    <property name="current.dir" value="." />
    <property name="assemblyJarFile" value="../target/scala-2.10/Typewriter-assembly-1.0.jar" />
    <property name="obfuscatedJarFile" value="tmp/TypewriterFX.jar" />
    <property name="tmp.dir" value="tmp" />

    <!-- output stuff -->
    <property name="release.dir" value="release" />

    <!-- CLEAN -->
    <target name="clean">
        <echo message="clean task ..." />
        <!-- just needed for the first-time setup -->
        <mkdir dir="${release.dir}"/>
        <mkdir dir="${tmp.dir}"/>
        <!-- remove old versions -->
        <delete dir="${release.dir}/Typewriter.app" />
        <delete file="${obfuscatedJarFile}"/>
    </target>


    <!-- OBFUSCATE -->
    <target name="obfuscate">
        <!-- proguard/disabled -->
        <!-- <proguard configuration="proguard.cfg"/> -->
    </target>


    <!-- CREATE MAC BUNDLE (new for Java7+) -->
    <!-- SEE intransitione.com/blog/take-java-to-app-store/ and -->
    <!--     docs.oracle.com/javase/7/docs/technotes/guides/jweb/packagingAppsForMac.html -->
    <target name="create-bundle" depends="clean, obfuscate">
        <bundleapp outputdirectory="${release.dir}"
            name="TypewriterFX"
            displayname="TypewriterFX"
            identifier="com.valleyprogramming.typewriter.Typewriter"
            shortversion="1.0"
            icon="NewIconMoreYellowBrighter.icns"
            copyright="Valley Programming"
            applicationCategory="public.app-category.utilities"
            mainclassname="com/valleyprogramming/typewriter/Typewriter">

            <runtime dir="${env.JAVA_HOME}" />

            <!-- the jar file created by sbt-assembly and obfuscated by proguard -->
            <!-- proguard/disabled -->
            <!-- <classpath file="${obfuscatedJarFile}" /> -->
            <classpath file="${assemblyJarFile}" />
            <classpath file="${scalaLibrary}" />

            <!-- use 'argument' here? see Info.plist, it has VMOptions and VMArguments -->

            <!-- jvm settings -->
            <option value="-Xms32m"/>
            <option value="-Xmx128m"/>
            <option value="-XX:PermSize=20m"/>
            <option value="-XX:MaxPermSize=20m"/>

            <!-- per http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html -->
            <!-- "use cms for desktop ui" -->
            <option value="-XX:+UseConcMarkSweepGC"/>
            <option value="-XX:ParallelCMSThreads=2"/>

            <option value="-Xdock:icon=Contents/Resources/NewIconMoreYellowBrighter.icns"/>
            <option value="-Dapple.laf.useScreenMenuBar=true"/>
            <option value="-Dcom.apple.macos.use-file-dialog-packages=true"/>
            <option value="-Dcom.apple.macos.useScreenMenuBar=true"/>
            <option value="-Dapple.awt.application.name=TypewriterFX"/>
            <option value="-Dcom.apple.smallTabs=true"/>

        </bundleapp>
    </target>

</project>

I normally put this script in a directory named deploy, which is just underneath my SBT project’s root directory. I actually write my code in Scala, not Java, so I use SBT. I also use a library named sbt-assembly to build only one Jar file for my entire application; that makes this build script much easier.

This build script creates a release directory beneath my deploy directory, and puts my freshly-built Mac application in it.

If you have some Ant experience, I hope that build file is pretty easy to read, though it will also help if you have experience with the AppBundlerTask as well.

Summary

As a summary, if you were looking for a way to control the RAM used by a Mac/Java/Scala application when building it with the AppBundlerTask, I hope this has been helpful. Hopefully it will also help with your general understanding of the JVM command line arguments, and which JVM to use for client applications.