www.adminn.cn
站长正能量分享网!

Java JVM常见面试题:JVM调优案例

AD:阿里云服务器企业会员更优惠 腾讯云香港,韩国免备案服务器1.8折优惠

GC调优策略

降低Minor GC频率

Minor GC的时间分为两部分:扫描新生代和复制存活对象时间。

如果堆中短期的对象很多,扩容新生代的大小(虽然会增加扫描新生代的时间,但同时也会缩短复制对象的时间,因为减少了存回对象的数量。对于老年代来说,进行Major,甚至是Full GC来说,所需要的时间也会减少。PS:扫描对象成本明显低于复制存活对象的成本)

如果堆中长期存活的对象很多,则不应该增加新生代的大小,因为会增加Minor GC的时间(扫描和复制存活对象的时间,都不会减少)

降低Full GC频率

减少大对象的创建

会超过所设置的大对象大小的阈值,直接进入老年代

即时没有超过所设置的大对象大小的阈值,在新生代空间有限的情况下,根据空间分配担保也会有大几率被分配到老年代

增大堆内空间

设置初始化堆内存和最大堆内存大小相同,也可以降低Full GC的频率。也会减少内存自动扩容、自动降容的成本。

选择适当的GC回收器

对单次操作的响应时间有优先的要求,可以选择CMS和G1收集器

堆系统吞吐量有优先的要求,可以选择Parallel Scavenge收集器

案例

大内存硬件上的程序部署策略

场景:一个统计/大文档系统,给HotSpot虚拟机分配2G内存,用户使用觉得网站比较慢,在运维把内存升到12G。

问题:经常发生无规律长时间失去响应。

原因:(Parallel Scavenge/Old收集器)过大的堆内存进行Full GC时,用户线程长时间停顿导致(之前服务的堆内存2G,在进行Full GC时,用户线程停顿时间稍断,所以用户在访问时,只是会觉得慢,但是把内存增加到12G之后,老年代在进行Full GC时,需要回收的内存变大,所需的时间就会增加,甚至会超过web服务器的响应时间,失去响应)。

部署方面(单机的情况下):

单独的Java虚拟机实例的缺点

回收大块堆内存而导致的长时间停顿(可以考虑G1收集器的增量回收)

大内存需要64位Java虚拟机,但是因为64位虚拟机的压缩指针、处理器缓存行容量的额外开销,性能会比32位虚拟机偏低。

保证应用程序的足够稳定。如果发生了OOM,堆转储快照的文件太大,信息量很难进行分析。甚至无法产生堆转储快照文件。

多个Java虚拟机,逻辑集群(负载均衡、反向代理)的缺点

节点竞争全局的资源(磁盘IO)

很难最高效率的利用连接池(分配不均)

大量使用本地缓存(各个节点都需要维护自己的缓存,存在内存浪费的情况)

最终总结:

优化代码层次:减少大对象的产生,或者不能有批量的、长生存时间的大对象产生。

虚拟机配置:监控观察大对象的大小,设置-XX:PretenureSizeThreshold的大小,来拦截进入对象老年区的机会。使其在新生代完成对象的生命周期,并改用CMS收集器。

建立多虚拟机节点的逻辑集群:每个虚拟机分配2G的内存。

服务器虚拟机进程崩溃

场景:基于B/S的MIS系统,双机6实例集群。

问题:运行期间频繁出现集群节点的虚拟机进程自动关闭。

原因:调用了第三方服务导致,由于第三方接口响应时间慢的原因,虽然使用了异步调用的方式,但是会有越来越多等待的线程和Socket连接,导致虚拟机进程崩溃

最终总结:使用生产者/消费者模式的消息队列

堆外内存导致的溢出错误

场景:websocket实时推送服务

问题:服务器不定时抛出内存溢出。(加大内存、堆转储快照分析、甚至Eden,Survivor,老年代,方法区的GC频次与时间都无问题)

原因:大量的NIO操作使用直接内存导致。

扩展:除了堆和方法区,服务还会占用其他的内存

直接内存,如NIO连接,可以设置-XX:MaxDirectMemorySize设置所使用的内存大小。即使在OOM的时候,也能产生堆转储快照。

线程堆栈:-Xss调整大小。

Socket缓存区:每个Socket连接都需要Receive和Send两个缓存区。

JNI代码:本地库使用的内存是占用了本地方法栈和本地内存。

虚拟机和垃圾收集器

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

场景:一个后台RPC服务器,使用64位Java虚拟机,内存配置为-Xms4g-Xmx8g-Xmn1g,使用ParNew加CMS的收集器组合。业务上需要每10分钟加载一个约80MB的数据文件到内存进行数据分析,这些数据会在内存中形成超过100万个HashMapEntry,在这段时间里面Minor GC就会造成超过500毫秒的停顿。

分析:ParNew收集器使用的是复制算法,这个算法的高效是建立在大部分对象都是存活时间很短的情况下,如果存活对象过多,把这些对象复制到Survivor并维持这些对象引用,导致垃圾收集的暂停时间明显变长。

总结:

GC调优:可以考虑直接将Survivor空间去掉(-XX:SurvivorRatio=65536、-XX:MaxTenuringThreshold=0),直接进入老年代,等到Major GC时区清理。

修改数据结构

HashMap的空间效率:

HashMap结构,Key和Value所存放的两个长整型数据是有效数据,共16字节。

用Long对象包装之后:

Long对象的空间,8字节的Mark Word、8字节的Klass指针,再加8字节存储数据的long值,共24字节。

2个Long对象组成Map.Entry之后,又多了16字节的对象头,然后一个8字节的next字段和4字节的int型的hash字段,为了对齐,还必须添加4字节的空白填充

HashMap中对这个Entry的8字节的引用

结论:空间效率,16字节/88字节=18%。

模板优惠价: (点击购买)
版权声明:本文采用知识共享 署名4.0国际许可协议 [BY-NC-SA] 进行授权
文章名称:《Java JVM常见面试题:JVM调优案例》
文章链接:https://www.adminn.cn/news/1985.html
本站资源模板仅供个人学习交流,请于下载后24小时内删除,不允许用于商业用途,否则法律问题自行承担。2021.5月起,网站调整,暂不再分享免费模板。谢谢理解

Adminn.Cn 站长分享圈

帝国CMS精品模板腾讯云优惠券,代金券

本站源码仅供本地环境下学习借鉴研究使用!

源码请勿用于任何涉灰站点!净化网络,站长更有责!

支付宝扫一扫打赏