文章目录
- 内存泄漏(memory leak)
- 内存溢出(Out of Memory):
- 内存泄漏的常见场景
- 场景一:未删除用户数据
- 场景二:分布式任务调度
- 解决内存溢出
- 解决内存溢出思路
- 发现问题 – Top命令
- 发现问题 – VisualVM
- 发现问题 – Arthas
- 使用arthas tunnel管理所有的需要监控的程序
- 发现问题 – Prometheus + Grafana
- 发现问题–堆内存状况的对比
内存泄漏(memory leak)
- 内存泄漏是指程序中的某个对象(或一组对象)被错误地保留在内存中,而无法被垃圾回收器回收。这些对象将继续占用内存,导致系统中的可用内存逐渐减少,最终可能导致内存耗尽。
- 内存泄漏绝大多数情况都是由堆内存泄漏引起的(所以没有特别说明,则讨论的都是堆内存泄漏)。
- 内存泄漏的常见原因包括:
- 未正确关闭资源: 如果程序打开了文件、网络连接、数据库连接等资源,但在使用完后未正确关闭,就可能导致内存泄漏。
- 循环引用: 当两个或多个对象相互引用,但它们之间没有被及时断开引用,就会导致这些对象无法被垃圾回收。
- 静态集合持有对象: 静态集合(如静态列表、Map等)可能在应用程序的整个生命周期内持有对象的引用,如果不及时清理,可能导致内存泄漏。
- 为了避免内存泄漏,开发者可以:
- 确保及时关闭使用的资源。
- 避免不必要的对象引用,特别是循环引用。
- 使用弱引用(WeakReference)等机制来管理对象的生命周期。
- 使用工具和分析技术(如内存分析工具)来检测和解决潜在的内存泄漏问题。
- 少量的内存泄漏可以容忍,但是如果发生持续的内存泄漏,内存迟早会被消耗完,最终导致的结果就是内存溢出。但是产生内存溢出并不是只有内存泄漏这一种原因。
内存溢出(Out of Memory):
-
内存溢出是指程序在运行过程中申请的内存超出了JVM(Java虚拟机)所能提供的最大内存限制,导致无法继续分配新的对象,最终导致程序崩溃。内存溢出通常发生在以下几种情况下:
- 无限制的对象创建: 如果应用程序在循环中创建大量对象而没有及时释放,可能导致内存溢出。
- 递归调用: 过深的递归调用也可能导致栈溢出,间接导致内存溢出。
- 内存泄漏: 长时间运行的应用程序中,如果存在内存泄漏,最终会导致内存溢出。
- 过大的数据集: 当应用程序试图加载过大的数据集时,可能会导致内存溢出。
-
为了避免内存溢出,可以:
- 优化代码,减少不必要的对象创建。
- 及时释放不再使用的对象,避免长时间保持引用。
- 调整JVM堆内存参数,增加可用的堆内存。
内存泄漏的常见场景
场景一:未删除用户数据
- 第一种常见场景是大型的Java后端应用中,在处理用户的请求之后,没有及时将用户的数据删除。随着用户请求数量越来越多,内存泄漏的对象占满了堆内存最终导致内存溢出。
- 这种产生的内存溢出会直接导致用户请求无法处理,影响用户的正常使用。重启可以恢复应用使用,但是在运行一段时间之后依然会出现内存溢出。
场景二:分布式任务调度
- 第二种常见场景是分布式任务调度系统如Elastic-job、Quartz等进行任务调度时,被调度的Java应用在调度任务结束中出现了内存泄漏,最终导致多次调度之后内存溢出。
- 这种产生的内存溢出会导致应用执行下次的调度任务执行。同样重启可以恢复应用使用,但是在调度执行一段时间之后依然会出现内存溢出
解决内存溢出
解决内存溢出思路
- 解决内存溢出的步骤总共分为四个步骤,其中前两个步骤是最核心的
发现问题 – Top命令
- top命令是linux下用来查看系统信息的一个命令,它提供实时地去查看系统的资源,比如执行时的进程、线程和系统参数等信息。
- 进程使用的内存为RES(常驻内存)- SHR(共享内存)
- 优点:操作简单;无额外的软件安装、
- 缺点:只能查看最基础的进程信息,无法查看到每个部分的内存占用(堆、方法区、堆外)
发现问题 – VisualVM
- VisualVM是多功能合一的Java故障排除工具并且他是一款可视化工具,整合命令行 JDK 工具和轻量级分析功能,功能非常强大
- 这款软件在Oracle JDK 6~8 中发布,但是在 Oracle JDK 9 之后不在JDK安装目录下需要单独下载。下载地址
- 优点:功能丰富,实时监控CPU、内存、线程等详细信息;支持Idea插件,开发过程中也可以使用
- 缺点:对大量集群化部署的Java进程需要手动进行管理
发现问题 – Arthas
- Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升线上问题排查效率。
- 优点:功能强大,不止于监控基础的信息,还能监控单个方法的执行耗时等细节内容;支持应用的集群管理
- 缺点:部分高级功能使用门槛较高
使用arthas tunnel管理所有的需要监控的程序
- 使用微服务架构,生产环境上的应用数量非常多,使用arthas还得登录到每一台服务器上再去操作非常不方便可以使用tunnel来管理所有需要监控的程序。
步骤:
- 在Spring Boot程序中添加arthas的依赖(支持Spring Boot2),在配置文件中添加tunnel服务端的地址,便于tunnel去监控所有的程序。官方指导文档
- 将tunnel服务端程序部署在某台服务器上并启动。
- 启动java程序
- 打开tunnel的服务端页面,查看所有的进程列表,并选择进程进行arthas的操作。
发现问题 – Prometheus + Grafana
- prometheus 是一套开源的系统监控报警框架。
- Prometheus+Grafana是企业中运维常用的监控方案,其中Prometheus用来采集系统或者应用的相关数据,同时具备告警功能。Grafana可以将Prometheus采集到的数据以可视化的方式进行展示
- Java程序员要学会如何读懂Grafana展示的Java虚拟机相关的参数。
- 优点:
- 支持系统级别和应用级别的监控,比如linux操作系统、Redis、MySQL、Java进程。
- 支持告警并允许自定义告警指标,通过邮件、短信等方式尽早通知相关人员进行处理
- 缺点:环境搭建较为复杂,一般由运维人员完成