java面试-深入理解JVM(六)——JVM性能调优实战

简介: 如何在高性能服务器上进行JVM调优?为了充分利用高性能服务器的硬件资源,有两种JVM调优方案,它们都有各自的优缺点,需要根据具体的情况进行选择。

如何在高性能服务器上进行JVM调优?

为了充分利用高性能服务器的硬件资源,有两种JVM调优方案,它们都有各自的优缺点,需要根据具体的情况进行选择。

1. 采用64位操作系统,并为JVM分配大内存

我们知道,如果JVM中堆内存太小,那么就会频繁地发生垃圾回收,而垃圾回收都会伴随不同程度的程序停顿,因此,如果扩大堆内存的话可以减少垃圾回收的频率,从而避免程序的停顿。

因此,人们自然而然想到扩大内存容量。而32位操作系统理论上最大只支持4G内存,64位操作系统最大能支持128G内存,因此我们可以使用64位操作系统,并使用64位JVM,并为JVM分配更大的堆内存。但问题也随之而来。

堆内存变大后,虽然垃圾收集的频率减少了,但每次垃圾回收的时间变长。如果对内存为14G,那么每次Full GC将长达数十秒。如果Full GC频繁发生,那么对于一个网站来说是无法忍受的。

因此,对于使用大内存的程序来说,一定要减少Full GC的频率,如果每天只有一两次Full GC,而且发生在半夜, 那完全可以接受。

要减少Full GC的频率,就要尽量避免太多对象进入老年代,可以有以下做法:

  • 确保对象都是“朝生夕死”的 
    一个对象使用完后应尽快让他失效,然后尽快在新生代中被Minor GC回收掉,尽量避免对象在新生代中停留太长时间。
  • 提高大对象直接进入老年代的门槛 
    通过设置参数-XX:PretrnureSizeThreshold来提高大对象的门槛,尽量让对象都先进入新生代,然后尽快被Minor GC回收掉,而不要直接进入老年代。 

注意:使用64位JDK的注意点

  1. 64位JDK支持更大的堆内存,但更大的堆内存会导致一次垃圾回收时间过长。
  2. 现阶段,64位JDK的性能普遍比32位JDK低。
  3. 堆内存过大无法在发生内存溢出时生成内存快照 
    若将堆内存设为10G,那么当堆内存溢出时就要生成10G的大文件,这基本上是不可能的。
  4. 相同程序,64位JDK要比32位JDK消耗更大的内存 

2. 使用32位JVM集群

针对于64位JDK种种弊端,我们更多选择使用32位JDK集群来充分利用高性能机器的硬件资源。


如何实现?

在一台服务器上运行多个服务器程序,这些程序都运行在32位的JDK上。然后再运行个服务器作为反向代理服务器,由它来实现负载均衡。 
由于32位JDK最多支持2G内存,因此每个虚拟结点的堆内存可以分配1.6G,一共运行10个虚拟结点的话,这台物理服务器可以拥有16G的堆内存。 

有啥弊端?

  1. 多个虚拟节点竞争共享资源时容易出现问题 
    如多个虚拟节点共同竞争IO操作,很可能会引起IO异常。
  2. 很难高效地使用资源池 
    如果每个虚拟节点使用各自的资源池,那么无法实现各个资源池的负载均衡。如果使用集中式资源池,那么又存在竞争的问题。
  3. 每个虚拟节点最大内存为2G


别忘了直接内存也可能导致内存溢出!

问题描述

有个小型网站,使用32位JDK,堆1.6G。运行期间发现老是出现内存溢出。为了判断是否是堆内存溢出,在程序运行前添加参数:-XX:+HeapDumpOnOutOfMemeryError(添加这个参数后当堆内存溢出时就会输出异常日至)。但当再次发生内存溢出时,没有生成相关异常日志。从而可以判定,不是堆内存发生溢出。 

问题分析

我们可以发现,在32位JDK中,将1.6G分配给了堆,还有一部分分配给了JVM的其它内存,只有少于0.4G的内存为非JVM内存。我们知道,如果使用了NIO,那么JVM会在JVM内存之外分配内存空间,这部分内存也叫“直接内存”。因此,如果程序中使用了NIO,那么就要小心“直接内存”不足时发生内存溢出异常了! 

直接内存的垃圾回收过程

直接内存虽然不是JVM内存空间,但它的垃圾回收也有JVM负责。直接内存的垃圾回收发生在Full GC时,只有当老年代内存满时,垃圾收集器才会顺便收集一下直接内存中的垃圾。 
如果直接内存已满,但老年代没满,这时直接内存先是抛出异常,相应的catch块中调用System.gc()。由于System.gc()只是建议JVM回收,JVM可能不马上回收内存,那么这时直接内存就抛出内存溢出异常,使得程序终止。


JVM崩溃的原因

当内存溢出时,JVM仅仅会终止当前运行的程序,那么什么时候JVM会崩溃呢? 

什么是异步请求?

我们知道,Web服务器和客户端采用HTTP通信,而HTTP底层采用TCP通信。异步通信就是当客户端向服务器发送一个HTTP请求后,将这个请求的TCP连接委托给其它线程,然后它转而做别的事,那条被委托的线程保持TCP连接,等待服务器的回信。当收到服务器回信后,再将收到的数据转交给刚才的线程。这个过程就是异步通信过程。 

异步请求如何造成JVM崩溃?

如果一个Web应用使用了较多的异步请求(AJAX),每次主线程发送完请求后都将TCP连接交给一条新的线程去等待服务器回信,那么如果网络不流畅时,这些受委托的线程迟迟等不到服务器的回信,因此保持着TCP连接。当TCP连接过多时,超过JVM的承受能力,JVM就发生崩溃。


如何处理大对象?

大对象对于JVM来说是个噩耗。如果对象过大,当前新生代的剩余空间装不下它,那么就需要使用分配担保机制,将当前新生代的对象都复制到老年代中,给大对象腾出空间。分配担保涉及到大量的复制,因此效率很低。

那么,如果将大对象直接放入老年代,虽然避免了分配担保过程,但该对象只有当Full GC时才能被回收,而Full GC的代价是高昂的。如果大对象过多时,老年代很快就装满了,这时就需要进行Full GC,如果Full GC频率过高,程序就会变得很卡。

因此,对于大对象,有如下几种处理方法: 
1. 在写程序的时候尽量避免大对象 
从源头降低大对象的出现,尽量选择空间利用率较高的数据结构存储。 
2. 尽量缩短大对象的有效时间 
对象用完后尽快让它失效,好让垃圾收集器尽快将他回收,避免因在新生代呆的时间过长而进入老年代。

相关文章
|
7天前
|
数据采集 消息中间件 监控
Flume数据采集系统设计与配置实战:面试经验与必备知识点解析
【4月更文挑战第9天】本文深入探讨Apache Flume的数据采集系统设计,涵盖Flume Agent、Source、Channel、Sink的核心概念及其配置实战。通过实例展示了文件日志收集、网络数据接收、命令行实时数据捕获等场景。此外,还讨论了Flume与同类工具的对比、实际项目挑战及解决方案,以及未来发展趋势。提供配置示例帮助理解Flume在数据集成、日志收集中的应用,为面试准备提供扎实的理论与实践支持。
21 1
|
10天前
|
Oracle Java 关系型数据库
java体系结构和jvm
java体系结构和jvm
|
21天前
|
缓存 Java C#
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍(一)
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍
60 0
|
1天前
|
缓存 监控 Java
深入理解Java虚拟机(JVM)性能调优
【4月更文挑战第18天】本文探讨了Java虚拟机(JVM)的性能调优,包括使用`jstat`、`jmap`等工具监控CPU、内存和GC活动,选择适合的垃圾回收器(如Serial、Parallel、CMS、G1),调整堆大小和新生代/老年代比例,以及代码优化和JIT编译策略。通过这些方法,开发者能有效提升应用性能并应对复杂性挑战。性能调优是持续过程,需伴随应用演进和环境变化进行监控与优化。
|
3天前
|
存储 XML 监控
JVM工作原理与实战(三):字节码文件的组成
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了字节码文件的基础信息、常量池、方法、字段、属性等内容。
|
1月前
|
存储 缓存 安全
[Java基础]——JVM内存模型
[Java基础]——JVM内存模型
|
1月前
|
算法 Java UED
【JVM】分代收集算法:提升Java垃圾回收效率
【JVM】分代收集算法:提升Java垃圾回收效率
19 0
|
1月前
|
Java
【JVM】深入理解Java引用类型:强引用、软引用、弱引用和虚引用
【JVM】深入理解Java引用类型:强引用、软引用、弱引用和虚引用
88 0
|
21天前
|
Java 程序员
java线程池讲解面试
java线程池讲解面试
38 1
|
2月前
|
存储 关系型数据库 MySQL
2024年Java秋招面试必看的 | MySQL调优面试题
随着系统用户量的不断增加,MySQL 索引的重要性不言而喻,对于后端工程师,只有在了解索引及其优化的规则,并应用于实际工作中后,才能不断的提升系统性能,开发出高性能、高并发和高可用的系统。 今天小编首先会跟大家分享一下MySQL 索引中的各种概念,然后介绍优化索引的若干条规则,最后利用这些规则,针对面试中常考的知识点,做详细的实例分析。
238 0
2024年Java秋招面试必看的 | MySQL调优面试题