JVMのパラメーター

参考にしたサイト
http://blog.ik.am/entry/view/id/85/title/JVM%E3%81%AE%E3%83%92%E3%83%BC%E3%83%97%E3%83%BBGC%E3%83%81%E3%83%A5%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B0%E3%81%BE%E3%81%A8%E3%82%81/
http://www.whitemark.co.jp/tec/java/javagc.html

抜粋---
■ 運用環境で是非設定しておくべきJava起動オプション

                                                                                            • -

 -server サーバーモードを有効化
 -Xms Javaヒープ初期サイズ。一般的に、-Xmxと同値にする
 -Xmx Javaヒープ最大サイズ
 -Xmn(-XX:NewSize) New領域初期サイズ。一般的に、-XX:MaxNewSizeと同値にする
 -XX:MaxNewSize New領域最大サイズ
 -XX:PermSize Permanent領域初期サイズ。一般的に、-XX:MaxPermSizeと同値にする
 -XX:MaxPermSize Permanent領域最大サイズ
 -verbose:gc(-Xloggc:path_to_file) GCログ出力を有効化
 -XX:+PrintGCTimeStamps GCログにタイムスタンプ(Java起動時からの経過時間)を出力
 -XX:+PrintGCDetails GCログを詳細に出力(New領域とJavaヒープそれぞれが
  どれだけ減ったか出力される)
 -XX:+PrintClassHistogram*1 SIGQUITシグナル受信時にヒープ統計情報を出力
  (出力時に強制的にフル GCが発生)。-verbose:gc(-Xloggc:path_to_file)と併用必須。
  なお、SIGQUITシグナルを送信するには、kill -3 を実行すればよい
 -XX:+HeapDumpOnOutOfMemoryError OutOfMemoryError発生時にヒープダンプを出力
  (Sun Java 1.4.2_12以上、1.5.0_07以上、1.6以上)

    • -

アプリケーションサーバーにおいて、Java Heapに4GBを割り当てる場合の参考値。

  • server

■メモリ設定系--------------------------------------------------------

  • Xms 4096M
  • Xms 4096M
  • Xmn 1024M
  • XX:MaxNewSize 1024M
  • XX:PermSize 256M
  • XX:MaxPermSize 256M
                                                                                                                                            • -

NewSizeは、SunVM場合、Heapの25%。
PermSizeは、Applicationによるが、AOPでリフレクションが多い場合は大きく積む。ASで64MB、Applicationで128MBの計算。

GCダンプ系-----------------------------------------------------------

  • verbose:gc
  • Xloggc:filename
  • XX:+PrintGCTimeStamps
  • XX:+PrintGCDetails
  • XX:+PrintClassHistogram
                                                                                                                                            • -

GC系-----------------------------------------------------------------

  • XX:SurvivorRatio=16
  • XX:MaxTenuringThreshold=32
  • XX:TargetSurvivorRatio=90
  • XX:+UseParNewGC
  • XX:+UseConcMarkSweepGC
  • XX:+CMSIncrementalMode
  • XX:+CMSIncrementalPacing
  • XX:CMSIncrementalDutyCycleMin=0
  • XX:CMSIncrementalDutyCycle=10
  • XX:+UseTLAB
  • XX:+CMSParallelRemarkEnabled
  • XX:+CMSClassUnloadingEnabled
                                                                                                                                              • -

UserTLABは、スレッド ローカルのオブジェクト割り当てブロックを使用します。これにより、共有ヒープ ロックに対する競合の発生が減少して、同時実行性が向上します。

Java reflectionとpermメモリ

http://www.ibm.com/developerworks/jp/java/library/j-nativememory-aix/

まとめ
 ・reflectionは、-Dsun.reflect.inflationThreshold=N(default 15)以上の呼び出しが行われると、クラスが自動生成されて高速なバイトコードアクセスとなる。
  それ以前は、低速なJNIアクセス。
 ・自動生成されたクラスは、Permメモリにロードされるので、reflectionにて大量のクラスが生成される場合、Permメモリを圧迫することになる(大量のPermが必要)。
 ・これを防ぐためには、「-Dsun.reflect.inflationThreshold=0」。
 ・PermのGCが行われると、「Unloading class sun.reflect.GeneratedMethodAccessor」が頻発することになる。
 ・”「-Dsun.reflect.noInflation=true」を指定すると、リフレクション・アクセサーはまったく拡張されなくなりますが、代わりに、バイトコード・アクセサーが何に対しても使用されます。”
  つまり、「-Dsun.reflect.noInflation=true」は、常にクラスが自動生成されるってことか・・・。
 ・JSPは、リフレクションを使用する。

JUNITからDataSourceを利用するためには

やることは、
 1. 自前でinitialcontextを作成して、JNDIにbindしてあげる。

JNDIへのbindingには、TomcatのNamingを利用するので、いつもの通り、pom.xmlに記述して取得する。
DataSourceの取得といったらconnectionの取得なので、ついでにcommons-dbcpを利用してconnection poolingするようなつくりにする。

pom.xml

        <dependency>
            <groupId>tomcat</groupId>
            <artifactId>naming-java</artifactId>
            <version>5.0.28</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>tomcat</groupId>
            <artifactId>naming-factory</artifactId>
            <version>5.5.23</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>tomcat</groupId>
            <artifactId>naming-resources</artifactId>
            <version>5.5.23</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>[1.,)</version>
            <scope>test</scope>
        </dependency> 


そうしたら、Google-guiceでDataSourceにBindするようにAbstractModuleを記述する。
ここでは、test用として自前でJNDIにbindするconfigureTestを追加で作成している。

public abstract class AbstractDataSourceModule extends AbstractModule {

    /**
     * commons-dbcpのプロパティを取得します。
     * 
     * @return
     */
    abstract protected Properties getProperties();

    /**
     * JNDI名称を取得します。
     * 
     * @return
     */
    abstract protected String getJndiName();

    protected void configureTest() {
        final String jndiNamePrefix = "java:/comp/env/";
        String absoluteJndiName = jndiNamePrefix + getJndiName();

        try {
            // commons-dbcpによるdataSourceの生成
            DataSource dataSource =
                BasicDataSourceFactory.createDataSource(getProperties());

            // Contextの生成
            System.setProperty(
                Context.INITIAL_CONTEXT_FACTORY,
                "org.apache.naming.java.javaURLContextFactory");
            System.setProperty(Context.URL_PKG_PREFIXES, "org.apache.naming");

            InitialContext ic = new InitialContext();
            ic.createSubcontext("java:");
            ic.createSubcontext("java:/comp");
            ic.createSubcontext("java:/comp/env");
            ic.createSubcontext("java:/comp/env/jdbc");
            ic.bind(absoluteJndiName, dataSource);

            // Bind IntialContext
            bind(Context.class).toInstance(ic);

            // Bind DataSource
            bind(DataSource.class)
                .annotatedWith(Names.named(getJndiName()))
                .toProvider(
                    JndiIntegration
                        .fromJndi(DataSource.class, absoluteJndiName));
            bind(DataSource.class).toProvider(
                JndiIntegration.fromJndi(DataSource.class, absoluteJndiName));
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (NamingException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void configure() {
        final String jndiNamePrefix = "java:/comp/env/";
        String absoluteJndiName = jndiNamePrefix + getJndiName();

        try {
            InitialContext ic = new InitialContext();

            // Bind IntialContext
            bind(Context.class).toInstance(ic);

            // Bind DataSource
            bind(DataSource.class)
                .annotatedWith(Names.named(getJndiName()))
                .toProvider(
                    JndiIntegration
                        .fromJndi(DataSource.class, absoluteJndiName));
            bind(DataSource.class).toProvider(
                JndiIntegration.fromJndi(DataSource.class, absoluteJndiName));
        } catch (NamingException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

AbstractDataSourceModuleを継承してOracle用のパラメーター設定をサブクラスに定義する。

public class OracleDataSourceModule extends AbstractDataSourceModule {

    Object[][] dbcpProperties =
        {
            {
                "driverClassName",
                "oracle.jdbc.pool.OracleConnectionPoolDataSource" },
            { "url", "jdbc:oracle:thin:@127.0.0.1:1521:orcl" },
            { "username", "ELLECTRA" },
            { "password", "PASSWORD" },
            { "initialSize", "30" },
            { "maxActive", "100" },
            { "maxIdel", "30" },
            { "maxWait", "500" } };

    String jndiName = "jdbc/hoge";

    @Override
    protected String getJndiName() {
        return jndiName;
    }

    @Override
    protected Properties getProperties() {
        Properties properties = new Properties();
        for (Object[] prop : dbcpProperties) {
            properties.put(prop[0], prop[1]);
        }
        return properties;
    }
}

さらにテスト用でconfigureTest()でconfigureするように書き換える。

public class OracleDataSourceModuleTest extends OracleDataSourceModule {

    @Override
    protected void configure() {
        configureTest();
    }
}

これで、前回のTestRunnerを利用すれば、ModuleにOracleDataSourceModuleを指定していも、JUnit時は、OracleDataSourceModuleTestが呼ばれる。

こういう暗黙的ルールは良いのか悩みどころです。

Google Guiceを利用したJUnit試験用のRunnerとAnnotation 改造

前回作成したTestRunnerに対して、Moduleのクラス名のsafixにTestが付いているものを優先して読み込むように改造した。
#テスト時は、xxxxを利用したいといった場合用です。

public class GuiceJunitRunner extends BlockJUnit4ClassRunner {

    protected final static Log _log = LogFactory.getLog(GuiceJunitRunner.class);

    public GuiceJunitRunner(Class<?> klass) throws InitializationError {
        super(klass);
    }

    @Override
    protected void validateZeroArgConstructor(List<Throwable> errors) {
        // nothing
    }

    @Override
    protected Object createTest() throws Exception {
        Class<?> cls = getTestClass().getJavaClass();
        GuiceStage guiceStage = cls.getAnnotation(GuiceStage.class);
        GuiceModule guiceModule = cls.getAnnotation(GuiceModule.class);

        Injector injector = null;

        List<Module> modules = new ArrayList<Module>();
        if (guiceModule != null)
            for (Class<?> moduleClass : guiceModule.value()) {
                try {
                    String testModule = moduleClass.getName() + "Test";
                    try {
                        moduleClass = Class.forName(testModule);
                    } catch (Exception e) {
                        ; // nothing
                    }
                    modules.add((Module) moduleClass.newInstance());
                } catch (InstantiationException e) {
                    if (_log.isErrorEnabled()) {
                        _log.error(MessageFormat.format(
                            "InstantiationException Module is {1}",
                            moduleClass.toString()), e);
                    }
                    throw e;
                } catch (IllegalAccessException e) {
                    if (_log.isErrorEnabled()) {
                        _log.error(MessageFormat.format(
                            "IllegalAccessException Module is {1}",
                            moduleClass.toString()), e);
                    }
                    throw e;
                }
            }

        if (guiceStage != null && guiceModule != null) {
            injector = Guice.createInjector(guiceStage.value(), modules);
        } else if (guiceStage != null) {
            injector = Guice.createInjector(guiceStage.value());
        } else if (guiceModule != null) {
            injector = Guice.createInjector(modules);
        } else {
            injector = Guice.createInjector(Stage.DEVELOPMENT);
        }

        return injector.getInstance(getTestClass().getJavaClass());
    }
}

Google Guiceでservlet

GuiceでServeletを利用するには、guice-servlet.jarが必要になります。
mavenのglobal repositoryにないので、ローカルリポジトリに登録します。

%maven install:install-file -Dfile=guice-servlet-3.0.jar -DgoupdId=com.google.guice -DartifactId=guice-servlet -Dversion=3.0 -Dpackaging=jar

webapp用のプロジェクトを作成する。

%mvn archetype:generate

maven3.0ですと対話型でできます。
最初のChoose a numberを112を指定します。

そして必要な項目をpom.xmlに設定します。
最低限だとこんな感じかな。
ポイントは、WEBコンテナがもっているjarは、scopeにprovidedを指定してdeployする対象から外す。

	<dependencies>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.3</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>commons-lang</groupId>
			<artifactId>commons-lang</artifactId>
			<version>[2.,)</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>[1.,)</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-core</artifactId>
			<version>[0.,)</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>[0.,)</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>[4.,)</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>com.google.inject</groupId>
			<artifactId>guice</artifactId>
			<version>[3.,)</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>com.google.inject</groupId>
			<artifactId>guice-servlet</artifactId>
			<version>[3.,)</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>com.google.guava</groupId>
			<artifactId>guava</artifactId>
			<version>[0.,)</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>commons-beanutils</groupId>
			<artifactId>commons-beanutils-core</artifactId>
			<version>[1.,)</version>
			<scope>runtime</scope>
		</dependency>
	</dependencies>
	<build>
		<finalName>pae-web</finalName>
		<sourceDirectory>src/main/java</sourceDirectory>
		<testSourceDirectory>src/test/java</testSourceDirectory>
		<pluginManagement>
			<plugins>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-eclipse-plugin</artifactId>
					<version>2.8</version>
					<configuration>
						<downloadSources>true</downloadSources>
						<downloadJavadocs>true</downloadJavadocs>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>
	</build>

いつも通り、eclipseのプロジェクトに変換しますが、ついで、WTP対応にしておくと便利なので、WTPの指定をします。

% mvn eclipse:clean eclipse:eclipse -Dwtpversion=2.0

web.xmlguiceのフィルターの設定をします。
guiceサーブレットの動作は、listenerでinitializeしたservletやfilterのマッピングをGuiceFilterでキャッチしてDispatchしてくれるというものらしい。

	<filter>
		<filter-name>GuiceFilter</filter-name>
		<filter-class>
			com.google.inject.servlet.GuiceFilter
        </filter-class>
	</filter>
	<filter-mapping>
		<filter-name>GuiceFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	
    <listener>
        <listener-class>jp.co.sandbox.GuiceServletConfig</listener-class>
    </listener>	

Guiceでのfilterやservletの指定の仕方

public class GuiceServletConfig extends GuiceServletContextListener {

    @Override
    protected Injector getInjector() {
        return Guice.createInjector(new ServletModule() {
            @Override
            protected void configureServlets() {
                serve("*.do2").with(MyServlet.class);
            }
        });
    }
}

サンプルの何もしないサーブレット

public class MyServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        System.out.println("enter doGet");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        System.out.println("enter d oPost");
    }
}

Google GuiceのProviderによるInjection

factory系で生成したオブジェクトをDIしましょうという機能らしい。
やり方は2通りで、Providerを実装するか、@Providesインスタンスで指定するか。

ただのhogeを返すProvider.

public class SampleProvider implements Provider<String> {
	public String get() {
		return "hoge";
	}
}

こちらは、上の"hoge"を返すプロバイダーを"sample"という名前付きでStringにbindする例と、@Providesを利用して、"Checkout"という名前付きでbindする例。
@Providesを利用した場合は、メソッドの戻り値のクラスにbindする。

public class SampleProviderModule extends AbstractModule {
	@Override
	protected void configure() {
		bind(String.class).annotatedWith(Names.named("Sample")).toProvider(SampleProvider.class);
	}

	/**
	 * Provider
	 * 
	 * @return
	 */
	@Provides
	@Named("Checkout")
	public String provideJiji() {
		return "jiji";
	}
}

利用する側はいつもの通り。

@Inject
@Named("Sample")
String sample;

@Inject
@Named("Checkout")
String jiji;