笔记:深入理解JVM 第5章 调优案例分析与实战
1、每天15万 PV 的在线文档类型网站
环境:4 CPU,16GB 内存, 64位 CentOS 5.4
问题:网站失去响应
原先JVM配置:JDK1.5, -Xmx12G -Xms12G
解决过程:发现问题来自GC停顿(12G内存 的 Full GC 需要12秒),内存中暂存文件导致“朝生夕灭”大量大对象,采用默认吞吐量优先收集器。
最终JVM配置:使用5个32位JDK作为逻辑集群,每个进程2GB内存,Apache服务作为前端负载均衡,垃圾回收改为CMS回收。
2、集群间同步导致的内存溢出
环境:两台服务器,每台2个CPU、8GB内存、启动3个WebLogic,构成6个节点的亲和式集群,JBossCache作为全局缓存
问题:内存溢出。
解决过程:配置-XX:+HeapDumpOnOutOfMemoryError ,检查heap文件,发现是JBossCache问题,缓存多次被写,。
最后结论:缓存可以频繁读,但是不能频繁写。
3、堆外内存导致的溢出
环境:1CPU,4GB内存,32位Window系统,小型B/S的电子考试系统,使用了CometD技术
问题:不定时抛出内存溢出异常
原先配置:
解决过程:加入-XX:HeapDumpOnOutOfMemoryError 无果,使用jstat 无果,查日志发现是Direct Memory 溢出:OutOfMemory:null, at sun.misc.Unsafe.allocateMemory (Native Method)。
CometD技术 用到了NIO 技术。Direct Memory 不能像老生代、新生代那样,发现空间不足了就通知收集器进行垃圾回收,他只能等待老生代Full GC的时候,顺便帮他清理内存的废弃对象,否则只能等待抛出异常。
最后结论:除了Heap 和 Perm ,这些区域也会占用内存: Direct Memory(-MaxDirectMemorySize)、线程栈 (-Xss)、Socket 缓存、JNI代码、虚拟机本身和GC
4、外部命令导致系统缓慢
环境:4CPU,Solaris,Glassfish
问题:请求响应慢,CPU使用率高
解决过程:通过Rumtime.getRuntime().exec() 执行外部shell脚本, 启动大量新进程,消耗大量时间。JVM执行Rumtime.getRuntime().exec() 流程:先复制一个与当前JVM拥有一样环境变量的进程,再使用这个新的进程去执行外部命令,最后退出,这种调用方式即使命令很快能执行完成,频繁创建进程的开销也非常可观。
解决方法:不使用shell 脚本,使用Java API。
5、服务器JVM进程崩溃
环境:2台服务器,2个CPU,8GB内存,WebLogic
问题:JVM自动关闭
解决过程:看日志,"Java.net.SocketException:Connection reset" 。因为远端连接到另外一个系统,调用另一个系统 完成Task,另一个系统处理速度非常慢,于是本系统积累了越来越多的等待线程和Socket连接。
解决方法:将调用另外一个系统的方式改成生产者-消费者模式的消息队列。
6、不恰当数据结构导致内存占用过大
环境:后台RPC服务器,64位虚拟机
原先JVM配置:-Xms4g -Xmx8g -Xmn1g -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
问题:加载80M的数据文件时候,每次Minor GC 造成停顿500 毫秒。
解决过程:看日志,新生代有多次Minor GC还存在的大对象
解决方法:去掉Survivor,-XX:+SurviorRatio=65536 ; 加入参数 -XX: MaxTenuringThreshold=0 -XX:+AlwaysTenure,将Minor GC后的对象立即进入老生代。修改程序。
7、Windows 虚拟内存导致长时间停顿
环境:带心跳检查的GUI桌面程序,每15秒一次心跳
问题:心跳检测有误,偶尔1分钟停顿
原先配置: -Xmx256M
解决过程:加入 -XX:PrintGCApplicaitonStoppedTime -XX:+PrintGCDateStamps -Xlooger:gclogs. 看日志 .程序在最小化时候,其工作内存被自动交换到磁盘的页面文件中
加入:-Dsun.awt.keepWorkingSetOnMinimize=true