docker 内运行的java服务
#进入docker
#docker 配置支持jmap命令
#0、docker-compose.yml 新增节点cap_add
cap_add:
- ALL
#1、找到对应的docker id
docker ps|grep yourserver
#2、进入docker 内部
docker exec -it e8c96f1fb92e sh
#3、使用jps 找到对应的java进程 id
jps
#4、jmap 命令的使用
jmap pid
jmap -clstats pid
jmap -head pid
jmap -heap pid
jmap -histo:live pid
jmap -finalizerinfo pid
#5、内存dump和分析
#5.1 导出整个JVM 中内存信息
jmap -dump:format=b,file=heapdump.hprof pid
#5.2 把docker的文件复制到主机
docker cp e8c96f1fb92e:/app/yourserver.hprof /data/log/
#把主机文件复制到docker内
docker cp /root/yourserver.hprof e8c96f1fb92e:/app/
#5.3 把文件下载到本地使用jprofile分析
#6、使用jstat命令查看jvm的GC情况
jstat -gc 30996 3000
#即:每3秒一次显示进程号为30996的java进程的GC情况
#或使用命令:jstat -gcutil 30996 3000
#发现大对象
com.mysql.cj.jdbc.AbandonedConnectionCleanupThread
com.mysql.cj.jdbc.AbandonedConnectionCleanupThread$ConnectionFinalizerPhantomReference
https://github.com/brettwooldridge/HikariCP/issues/1473
https://stackoverflow.com/questions/6981564/why-must-the-jdbc-driver-be-put-in-tomcat-home-lib-folder/7198049#7198049
JDBC drivers register themselves in the JVM-wide singleton [`DriverManager`](http://download.oracle.com/javase/6/docs/api/java/sql/DriverManager.html) which is shared by *all* web apps. If you have the same (as in class name) JDBC driver register twice from two different web apps, this might cause your problem. This is even more problematic if your web apps use different versions of the same JDBC driver.
Also, putting JDBC drivers into Tomcat's lib folder will help prevent memory leaks when you redeploy your web app without restarting Tomcat, e.g. if you just put a new WAR file into Tomcat's webapps folder:
We can produce the phenomenon by setting the maxLifetime to a relatively small value like a minute(all other configs are using the default value). Using tools like VisualVM, we can see an obvious increasement of `AbandonedConnectionCleanupThread$ConnectionFinalizerPhantomReference` objects.
其他类型内存泄漏
HashMap 的 put 过程中有可能出现了死循环问题(图中 java.util.HashMap $Entry 0x2add6d992cb8 和 0x2add6d992ce8 的 next 引用形成循环)。
查阅相关文档定位这属于典型的并发使用的场景错误 (http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6423457) ,简要的说就是 HashMap 本身并不具备多线程并发的特性,在多个线程同时 put 操作的情况下,内部数组进行扩容时会导致 HashMap 的内部链表形成环形结构,从而出现死循环。
If a HashMap is used in a concurrent setting with insufficient synchronization, it is possible for the data structure to get corrupted in such a way that infinite loops appear in the data structure and thus get() could loop forever.
详情参考:
[https://developer.51cto.com/art/202004/614586.htm](https://developer.51cto.com/art/202004/614586.htm)