JVM、OOM、内存泄漏、内存溢出、GC
背景接上篇《JVM调优实战(一)》,本节主要介绍生产环境常见的异常类型及处理办法。从实战的角度细说:如何发现问题、分析问题、解决问题。
文章导读生产问题在生产环境中,JVM可能出现,如内存泄漏、内存溢出、线程死锁、GC停顿和性能问题等。
如何处理CPU负载飙高?如何处理内存溢出?如何合理分配服务器内存?如何合理分配线程数?如何选择合适的垃圾回收器?如何优化减少频繁Full GC?......
出现上述问题后,势必会引起响应变慢、接口超时、系统卡顿等。通常,我们要分析具体业务场景。并通过系统监控发现问题。
调优步骤发现问题(性能监控)1、性能指标
响应时间(RT): 提交一次请求和返回该请求的响应之间使用的时间,一般指平均响应时间.常见操作例如:打开一个网站;查询一条SQL;一次远程调用请求;下游系统数据同步等吞吐量: 吞吐量是指在单位时间内系统能处理的请求数量,它体现了系统处理请求的能力,通常反映的是服务器负载的能力。QPS: 即每秒查询率,是评估系统查询处理能力的关键指标。 QPS = 总查询次数 / 总时间(秒).该指标反映系统在单位时间内能够成功处理的查询请求数量.并发数: 是指系统在同一时刻实际处理的请求或事务的数量,反映了系统可以同时承载的正常使用系统功能的用户的数量。一般来说,并发数越高,系统的处理能力越强。2、性能问题
频繁Full GCCPU负载高死锁OOM内存泄漏这里给出一个重要的top命令。主要用于排查:内存不断增长,CPU占用率居高不下的进程。 top 查看所有的进程的cpu、内存占比。 top -Hp pid 查看指定pid下各个线程的cpu、内存占比。
$ topPID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 9757 root 20 0 5173452 140856 14168 S 99.7 1.8 66:19.52 java 分析问题(性能分析)命令行工具,jstack,jmap,jinfo等导出heapdump.hprof文件,使用内存分析工具MAT分析使用JVisualVM或者阿里Arthas来实时查看JVM状态打印GC日志,通过GCviewer或者 gceasy.io来分析日志信息解决问题(性能调优)优化代码,比如循环调用、批量处理等。加机器节点,提高负载能力使用中间件提高吞吐量,比如缓存,异步等通过调整参数配置,例如:合理增加线程数选取合适的垃圾回收器复盘问题(性能考量)在业务系统中,各种场景都有可能导致系统性能问题。例如:
定时任务处理日增量数据:如果定时任务执行频率过高或处理逻辑复杂,可能导致系统资源(如CPU、内存、I/O)的频繁占用,从而影响其他任务的执行性能。此外,如果任务处理时间过长,还可能影响系统的响应时间。远程第三方平台调用:可能涉及网络延迟和数据传输。如果第三方平台响应缓慢或数据传输量大,可能导致系统等待时间过长,从而影响整体性能。微服务之间接口调用:微服务架构中,服务间的调用可能涉及网络通信和序列化/反序列化操作,这些操作都会带来一定的性能开销。如果服务调用链过长或调用频繁,可能导致系统性能下降。同时,不同服务之间的负载均衡和容错机制也会影响整体性能。远程获取Redis和MQ消息:从Redis和MQ中获取消息时,如果网络延迟较高或消息队列积压严重,可能导致系统等待时间过长,从而影响性能。此外,频繁地从Redis中读取或写入数据也可能导致性能瓶颈。批量处理割接数据:批量处理大量数据可能导致系统资源(如内存、CPU)的占用率急剧上升,从而影响其他任务的执行。大文件上传下载:处理大文件上传下载时,如果文件过大或网络带宽有限,可能导致上传下载速度缓慢,从而影响用户体验和系统性能。同时,处理大文件还可能带来磁盘I/O和内存占用的压力。复杂业务接口查询:复杂的业务接口查询可能涉及多表联查、大量数据计算和聚合等操作,这些操作都可能消耗大量的系统资源,从而影响性能。通常在各种应用场景中,我们不仅要对线上产生的问题进行复盘、分享、交流。而且要保持对业务的深刻理解和上线之前的数据量预估,进而对系统性能做出考量。
知道上述的一些基本步骤。接下来,重点聊聊关于JVM性能调优-OOM的案例。
性能优化案例基础配置主机参数:4核8GOracle jdk版本:1.8.0_221本节演示主机参数:4核8G。真实生产环境可能配置较高,具体在Linux系统下查看内存和CPU核数命令如下
查看内存:
free -h: 显示内存信息cat /proc/meminfo: 显示内存的详细信息,包括总内存、可用内存等。查看CPU核数:
nproc命令直接输出CPU核心数,是最简单的方法。lscpu命令可以显示CPU的详细信息,包括CPU核心数、架构等。cat /proc/cpuinfo文件也可以获取CPU的相关信息,如物理CPU个数、每个物理CPU的核数等,可以通过grep命令过滤出需要的信息,如cat /proc/cpuinfo | grep "processor" | wc -l可以查看逻辑CPU的个数。案例一、OOM-堆内存溢出
异常提示:java.lang.OutOfMemoryError: Java heap space
示例代码:
List list = new ArrayList(); while (true) { list.add(new Object()); }参数配置:
官方参考文档:docs.oracle.com/javase/8/do…
这里为了演示效果,为自定义配置
参数配置: 初始 -Xms20m 最大 -Xmx20mjava -jar -Xms20m -Xmx20m -XX:MetaspaceSize=8m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap/heapdump.hprof -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:log/gc-oom-heapspace.log demo-jvm-1.0-SNAPSHOT.jar演示结果:
监控与分析: MAT 分析dump文件
查看日志文件,也可用工具查看
less -SR /root/log/gc-oom-heapspace.log原因分析:
1、代码中可能存在大对象分配;
2、可能存在内存泄漏,导致在多次GC之后,还是无法找到一块足够大的内存容纳当前对象。
解决办法:
1、通过问题定位检查是否存在大对象的分配,可能的是大数组或者死循环;
2、使用MAT等工具分析堆内存dump文件,检查是否存在内存泄漏;
3、如果没有找到明显的内存泄漏,使用 -Xmx 加大堆内存
4、容易忽视的是:也有可能是使用框架内部造成的
发生场景:
场景 1:大数据处理当应用程序需要处理大量数据,并且这些数据无法全部加载到内存中时,可能会导致堆内存溢出。
案例代码:
public class BigDataProcessor { public static void main(String[] args) { List bigDataList = new ArrayList(); // 假设这是从数据库或文件中读取的大量数据 for (int i = 0; i MAX_CACHE_SIZE) { cache.remove(cache.keySet().iterator().next()); } } private ImageFeature extractFeature(byte[] imageData) { // 模拟图像处理,提取特征 return new ImageFeature(); } // ImageFeature 类定义 static class ImageFeature { // 特征数据 } public static void main(String[] args) { ImageProcessor processor = new ImageProcessor(); // 处理图片 for (int i = 0; i