JVM知识点——内存溢出和内存泄漏

面试苏宁时,问的,内存溢出怎么处理?

处理思路:先救火,再防火。

首先dump JVM的内存信息,这个信息用于后续的问题分析。如果重启了服务,JVM也会重启,这个信息就会丢失,所以务必先备份JVM的内存信息!!!

    总的来说,是通过jmap命令来备份JVM的内存信息,同时,需要我们了解进程的pid。

1、获取进程的pid:

ps -e | grep "NAME"
    其中,NAME指的是进程的名字。执行命令得到的信息中,包含pid,可以根据进程的信息找到对应的pid。

    当然,如果觉得查询出现的内容太多,也可以指定显示哪一列。例如,我的进程名字是“dialogue”,上面查询显示的第二列信息是pid,所以我的命令可以如下输入:

ps -e | grep "dialogue" | awk '{print $2}'
2、备份内存信息:

jmap -dump:format=b,file=[fileName] [pid]
    jmap命令可以用来获取JVM堆内存的信息,jmap -dump用来dump堆内存的信息。fileName指的是备份的堆内存信息保存的文件名,pid表示需要备份的进行的pid。例如,我的进程pid是73452,想要将堆内存信息保存在当前目录的jvm_heap.dump文件中,可以执行以下命令:

jmap -dump:format=b,file=jvm_heap.dump 73452
 另外,如果想要直接查看进程当前的堆内存信息,也可以使用jmap命令:

1.JVM里边有什么?(运行时的数据区)

首先我们只有知道JVM运行时的数据区,我们才能去一一诊断哪些地方可能会带来内存溢出以及内存泄漏的问题。

  • 程序计数器
  • 虚拟机栈
  • 本地方法栈
  • 方法区

【!!!下面的公式很重要!!!】

JVM内存【线程数*(最大栈容量)+最大堆值+其他内存(忽略不计或者一般不改动)】= 机器最大内存 - 直接内存

1.1 程序计数器(唯一一个没有内存溢出问题)

由于Java的多线程是通过线程轮流切换完成的,一个线程没有执行完时就需要一个东西记录它执行到哪了,下次抢占到了CPU资源时再从这开始,这个东西就是程序计数器,正是因为这样,所以它也是“线程私有”的内存。

1.2 Java虚拟机栈(可能会有内存溢出,几率很小)

虚拟机栈是线程私有的,是Java的方法执行的内存模型,执行时会创建栈帧,里边放了一大堆东西,有局部变量表,操作数栈,动态链接,方法出口等信息。

1.3 本地方法栈

虚拟机中使用了Native方法。

 栈溢出(StackOverflowError)栈容量由-Xss指定

原因:方法运行时,栈的深度超过了虚拟机容许的最大深度所致

这里要思考两个问题:当栈空间无法继续分配时,到底是栈内存太小,还是已经使用的栈空间太大?

解决思路:

 一)、是否有递归调用

二)、是否有大量循环或死循环

三)、全局变量是否过多

四)、 数组、List、map数据是否过大

1.4 堆(线程共享)通过-Xmx和-Xms控制

JVM里最大的一块内存区域,存放对象和数组。堆 = 新生代+老生代

1.4.1 堆内存溢出 java.lang.OutOfMemoryError:Java heap space

解决办法:

一)、检查代码,代码没有问题情况下,适当调整-Xms和-Xmx两个jvm参数,然后压测调整成最优值;

二)、避免大的对象内存申请,像文件上传、大批量从数据库获取,尽量分块分批操作;

三)、尽量提高一次请求的执行速度,垃圾回收越早越好;

1.4.2 堆内存泄漏 java . lang.OutOfMemoryError

描述:对象不再被使用,但是没有被垃圾收集识别,造成大量堆积,触发异常。

内存泄漏可以通过完善代码来避免。

某个业务系统在一段时间突然变慢,我们怀疑是因为出现内存泄露问题导致的,于是踏上排查之路。

采用下面的步骤分析:
1. 用工具生成java应用程序的heap dump(如jmap)
2. 使用Java heap分析工具(如MAT),找出内存占用超出预期的嫌疑对象
3. 根据情况,分析嫌疑对象和其他对象的引用关系。
4. 分析程序的源代码,找出嫌疑对象数量过多的原因。

1.5 方法区

方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。

1.5.1 方法区和运行时常量池溢出

1.6及之前的版本,会报PermGen Space

1.7及之后,会报Java heap space

1.5.2 元空间内存溢出

系统的代码非常多或引用的第三方包非常多或者通过动态代码生成类加载等方法,导致元空间的内存占用很大。

1)优化参数配置,避免影响其他JVM进程

2)慎重引用第三方包

3)关注动态生成类的框架

2.其他内存溢出

2.1 直接内存溢出

JVM虚拟机是运行在操作系统上的进程,操作系统分配给JVM的内存在启动是有限的,不可能把全部内存都分配给JVM,Java NIO又用到了直接内存技术,利用Channel和Buffer直接操作JVM外的内存,避免数据在JVM和操作系统内存之间来回复制。但是,当JVM和直接内存的和大于操作系统总内存时,就会发生内存溢出。

解决方法:可以考虑设置参数:-XX:MaxDirectMemorySize,并及时clear内存。

2.2 创建本地线程内存溢出

线程基本只占用heap以外的内存区域,也就是这个错误说明除了heap以外的区域,无法为线程分配一块内存区域了,这个要么是内存本身就不够,要么heap的空间设置得太大了,导致了剩余的内存已经不多了,而由于线程本身要占用内存,所以就不够用了。

解决方法:首先检查操作系统是否有线程数的限制,使用shell也无法创建线程,如果是这个问题就需要调整系统的最大可支持的文件数。

日常开发中尽量保证线程最大数的可控制的,不要随意使用线程池。不能无限制的增长下去。

2.3 超出交换区内存溢出

2.4 数组超限内存溢出

2.5 系统杀死进程内存溢出

猜你喜欢

转载自blog.csdn.net/qq_23079393/article/details/89601815