阅读JVM高级特性与最佳实践-3

调优案例分析与实战

理论是基础,实践是真理

GC停顿耗时长

4CPU、16G内存、centos 64位,一个15PV/天的文档类系统,堆内存设置

堆内存固定在12G
-Xms 12G
-Xmx 12G

vm监控显示Full GC频繁;原因,文档数据占用空间大,MiniorGC后,大量大对象去往老年代,导致老年代迅速达到Full GC出发点;大堆设置的情况适用于大多数对象都有朝生夕灭特性,这样可以在凌晨手动出发Full GC,使老年代一直处于一个稳定的状态。
64位相对于32位面临的问题

大内存回收导致的GC耗时长
产生堆溢出无法产生快照来进行问题分析
由于数据类型补齐导致64位消耗内存比32位大

window下32位最大内存限制为2G,jvm一般可以设置为1.5G,linux下可以设置为3G-4G,但是收到3^32限制;我觉得可以通过分布式缓存可以解决此问题,热点信息从缓冲中取,不得已再加载至jvm,控制在凌晨来手动GC,保证老年代的稳定。

堆外内存溢出

堆外内存,在小型项目中(I5 CPU 32位,4GB内存),在一些实时性要求比较高的情况,采用逆推送技术,comet中大量的NIO实现实时推送客户端需要信息,最后出现OOM异常,用jstat观察系统GC情况也非常稳定,加上-XX:+HeapDumpOnOutOfMemoryError,溢出后查看异常堆栈

java.lang.OutOfMemoryError: null
........................
at java.nio.DirectByteBuff.<init>(DirectByteBuff.java 99)
.........................

DirectByteBuff为对外内存,就是不占用你给JVM分配的内存,但是又会在JVM Full GC时进行回收,此案例中Full GC没有触发,但是堆外内存已经不足(如jvm 1.6G 对外就只剩下0.4G,特别是加上-XX:+DisableExplicitGC后),

除了java堆和永久代,还有如下占用内存
-XX:+MaxDirectMemorySize调整Direct Memory大小 ,不够时Direct buffer memory异常上述案例就可以通过调节合适的大小来解决此问题
-Xss 线程堆栈,无法申请新的空间则出现unable to create new native thread socket缓冲区:每个socket都有receive和send两个缓冲区,占用37KB和25KB内存,连接多的话内存占用也比较大,如果无法分配则出现IO异常:too many open files异常,这个异常在linux下面很常见,大量socket请况下(比如说netty、mina、nginx)产生,linux默认是1024
虚拟机和GC:虚拟机、GC执行都需要消耗一定的内存

外部命令导致系统缓慢

比如在linux环境下,java系统中存在通过Runtime.getRuntime().exec()方法调用shell脚本,用来获取系统信息;结果发现在并发测试情况下,系统响应非常慢且CPU占用非常高;更奇怪的是却不是jvm占用的cpu资源;最后发现最消耗资源的是fork进程,查找CPU飙升的位置,发现存在外部shell调用,而调用shell脚本时系统需要频繁的创建进程,忽略掉脚本本身执行时间,系统创建进程消耗的资源非常可怕,建议采用java代码获取系统信息

服务器jvm进程崩溃

在系统间交互过程中,两个系统处理资源性能不对等的情况下产生,如A系统给B系统推送消息,但是由于推送消息数量多B系统处理不过来,导致信息堆积在A系统,A在调用B服务后后等待很长时间才返回消息,并且全是连接中断;建议采用生产者消费者MQ消息队列来解决(java.net.SocketException: Connection reset),进行服务端和消费端端的解耦

不恰当的数据结构导致内存占用过大

RPC服务器,64Bit,-Xms4G -Xmx8G -Mmn1G,使用ParNew+CMS收集器组合。平时都是MinorGC,30毫秒完成;但是业务需求将80M的数据文件到内存进行数据分析,结果出现100W级别Map<Long,Long>,造成MinorGC500毫秒的停顿;ParNew使用的复制算法(针对朝生夕灭的特性);如果存活的大对象过多,就会有大量对象拷贝举动,导致STW;
一般解决:jvm参数加入-XX:SurvivorRatio=65535、-XX:MaxTenuringThreshold=0或者-XX:+AlwaysTenure,让eden存活对象第一次GC直接进入老年代(治标不治本),这样会导致老年代极不稳定,出现FULL GC的风险;

hashmap空间利用率分析,key和value占用16B,但是Long封装后就多了8B的MarkWord、8B的Klass指针,在加上存储long值得8B,两个Long对象组成的Map.Entry,又多了16B的对象头信息,然后一个8B的next字段和4B的int
hash字段(都会增加4B的空白填充),最后hashmap中对entry8b的引用,实际消耗内存88B,利用率为16/88=18%,利用率非常低;

是否可以考虑用其他方式代替文件分析,或者小文件分析,然后进行统计,避免MiniorGC时,大量存活对象的拷贝,导致系统延时

window虚拟内存导致GC时长

GUI程序中(-XX:+PrintGCApplicationStoppedTime -XX:PrintGCDateStamps -Xloggc:gclog.log -XX:+PrintReferenceGC),GUI程序在最小化时工作内存被交换到磁盘的页面文件中,这样再发生GC时因为恢复页面导致不正常的GC停顿,解决=Dsun.awt.keepWorkingSetOnMinimize=true

Eclipse运行速度调优

这是本地IDE启动参数配置

-startup
plugins/org.eclipse.equinox.launcher_1.3.100.v20150511-1540.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.1.300.v20150602-1417
-product
org.eclipse.epp.package.jee.product
--launcher.defaultAction
openFile
--launcher.XXMaxPermSize
256M
-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
256m
--launcher.defaultAction
openFile
--launcher.appendVmargs
-vmargs
-Dosgi.requiredJavaVersion=1.7
-Xms256m
-Xmx1024m
-Xmn128m
-Xverify:none  关闭字节码校验
-XX:+PrintGCTimeStamps
-XX:+PrintGCDetails
-XX:+DisableExplicitGC //关闭System.gc(),Full GC
-XX:+UseConcMarkSweepGC //CMS收集器也被称为短暂停顿并发收集器。它是对年老代进行垃圾收集的。CMS收集器通过多线程并发进行垃圾回收,尽量减少垃圾收集造成的停顿。
-XX:+UseParNewGC //置年轻代为多线程收集。可与CMS收集同时使用。在serial基础上实现的多线程收集器
-Xnoclassgc //不进行无用类的卸载
-XX:CMSInitiatingOccupancyFraction=85//老年代占用率达到多少时才进行垃圾回收
-verbose:gc//输出虚拟机GC详情
-Xloggc:F:\\worksoftware\\eclipse\\gc.log//保存GC日志到文件
 -Xbootclasspath/a:lombok.jar
-javaagent:lombok.jar
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值