OutOfMemoryするwarをPayaraに食わせてヒープダンプを吐かせる
-XX:+HeapDumpOnOutOfMemoryErrorのJVMオプションを試してみたかったのです。
war準備
こちら で 提供されているサンプルアプリケーションを使わせてもらいます。
hello.warをダウンロードして任意のフォルダに移動します。
$ wget https://javaee.github.io/glassfish/downloads/quickstart/hello.war
$ mkdir -p /tmp/volume/hello
$ mv hello.war /tmp/volume/hello/
$ chmod -R 777 /tmp/volume/
$ ll /tmp/volume/hello/
total 16
drwxrwxrwx 2 ubuntu ubuntu 4096 Mar 21 13:51 ./
drwxrwxrwx 3 ubuntu ubuntu 4096 Mar 21 13:50 ../
-rwxrwxrwx 1 ubuntu ubuntu 4102 Feb 5 2020 hello.war*
準備したディレクトリをマウントしてPayaraコンテナを起動します。
$ docker run --name payara -d -p 4848:4848 -p 8080:8080 -v /tmp/volume:/tmp/volume payara/server-full:6.2024.3-jdk21
コンテナ内でjarコマンド実行してwarを解凍します。
$ docker exec -it payara bash
payara@8462b1e630e7:~$ cd /tmp/volume/hello/
payara@8462b1e630e7:/tmp/volume/hello$ jar xvf hello.war
created: META-INF/
inflated: META-INF/MANIFEST.MF
created: WEB-INF/
created: WEB-INF/classes/
inflated: WEB-INF/classes/LocalStrings.properties
created: images/
inflated: WEB-INF/sun-web.xml
inflated: WEB-INF/web.xml
inflated: images/duke.waving.gif
inflated: index.jsp
inflated: response.jsp
不要なファイルを削除してindex.jspを以下の内容に書き換えます。
$ rm -rf images hello.war response.jsp
$ vi index.jsp
$ cat index.jsp
<%@ page import="java.util.ArrayList" %>
<html>
<head><title>Hello</title></head>
<body>
<%
new ArrayList(Integer.MAX_VALUE);
%>
</body>
</html>
再度コンテナ内に入りwarを作成します。
$ docker exec -it payara bash
payara@8462b1e630e7:~$ cd /tmp/volume/hello/
payara@8462b1e630e7:/tmp/volume/hello$ ls -l
total 12
-rw-r--r-- 1 payara payara 153 Mar 21 14:06 index.jsp
drwxr-xr-x 2 payara payara 4096 Oct 18 2005 META-INF
drwxr-xr-x 3 payara payara 4096 Oct 18 2005 WEB-INF
payara@8462b1e630e7:/tmp/volume/hello$ jar cvf ../hello-oom.war .
added manifest
ignoring entry META-INF/
ignoring entry META-INF/MANIFEST.MF
adding: WEB-INF/(in = 0) (out= 0)(stored 0%)
adding: WEB-INF/classes/(in = 0) (out= 0)(stored 0%)
adding: WEB-INF/classes/LocalStrings.properties(in = 120) (out= 102)(deflated 15%)
adding: WEB-INF/sun-web.xml(in = 387) (out= 265)(deflated 31%)
adding: WEB-INF/web.xml(in = 438) (out= 272)(deflated 37%)
adding: index.jsp(in = 153) (out= 126)(deflated 17%)
ひとつ上の階層にwarができていると思います。
$ ll /tmp/volume/
total 16
drwxrwxrwx 3 ubuntu ubuntu 4096 Mar 21 14:09 ./
drwxrwxrwt 14 root root 4096 Mar 21 14:09 ../
drwxrwxrwx 4 ubuntu ubuntu 4096 Mar 21 14:06 hello/
-rw-r--r-- 1 ubuntu ubuntu 1820 Mar 21 14:09 hello-oom.war
これでOOMするwarが作成できました。
Payaraの準備
先ほど起動したコンテナをそのまま使います。
Payaraにオプションを追加して、作成したwarをデプロイします。
$ docker exec -it payara asadmin -u admin create-jvm-options "-XX\:+HeapDumpOnOutOfMemoryError"
Do you trust the above certificate [y|N] -->y
Enter admin password for user "admin">
Created 1 option(s)
Command create-jvm-options executed successfully.
$ docker exec -it payara asadmin -u admin create-jvm-options "-XX\:HeapDumpPath\=\$\{com.sun.aas.instanceRoot\}/logs/heapdump.hprof"
Enter admin password for user "admin">
Created 1 option(s)
Command create-jvm-options executed successfully.
$ docker exec -it payara asadmin -u admin deploy /tmp/volume/hello-oom.war
Enter admin password for user "admin">
Application deployed with name hello-oom.
Command deploy executed successfully.
JVMオプションを反映させるためにコンテナを再起動します。
$ docker restart payara
テスト
ブラウザでhttp://<コンテナを起動したサーバーのIP>:8080/helloにアクセスすると、
以下の画面になると思います。
オプションで指定したディレクトリにダンプファイルができていると思います。
$ docker exec -it payara bash
payara@4b81a2a50a00:~$ cd appserver/glassfish/domains/domain1/logs/
payara@4b81a2a50a00:~/appserver/glassfish/domains/domain1/logs$ ls -lh
total 310M
-rw------- 1 payara payara 310M Mar 23 08:01 heapdump.hprof
-rw-r--r-- 1 payara payara 9.6K Mar 23 07:58 server.log
コンテナのログを見ると以下のメッセージが出力されていると思います。
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
Dumping heap to /opt/payara/appserver/glassfish/domains/domain1/logs/heapdump.hprof ...
Heap dump file created [324412621 bytes in 1.607 secs]
[#|2024-03-23T08:01:26.500+0000|WARNING|Payara 6.2024.3|jakarta.enterprise.web|_ThreadID=84;_ThreadName=http-thread-pool::http-listener-1(3);_TimeMillis=1711180886500;_LevelValue=900;|
StandardWrapperValve[jsp]: Servlet.service() for servlet jsp threw exception
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
at java.base/java.util.ArrayList.<init>(ArrayList.java:156)
at org.apache.jsp.index_jsp._jspService(index_jsp.java:55)
at org.glassfish.wasp.runtime.HttpJspBase.service(HttpJspBase.java:68)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:614)
at org.glassfish.wasp.servlet.JspServletWrapper.service(JspServletWrapper.java:317)
at org.glassfish.wasp.servlet.JspServlet.serviceJspFile(JspServlet.java:358)
at org.glassfish.wasp.servlet.JspServlet.service(JspServlet.java:287)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:614)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1554)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:259)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:166)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:757)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:577)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:158)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:372)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:239)
at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:520)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:217)
at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:174)
at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:153)
at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:196)
at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:88)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:246)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:178)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:118)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:96)
at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:51)
at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:510)
at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:82)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:83)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:101)
|#]
ダンプファイルはMemory Analyzer
で開けました。