为什么会内存溢出?内存溢出了怎么办?

为什么会内存溢出?

要么内存分配的太小了
要么代码写的有问题,生成的对象太多,没有即时回收垃圾对象。

一般都是第二种情况

如何防止OOM?

我们可以在以下这些方面努力:

自己写代码一定要注意:比如要避免死循环、防止递归爆栈、尽量避免长时间等待外部资源(数据库、网络、设备资源等)的情况

不要写多层循环,开发中见到有人写四层循环的,简直了。我的开发原则是最多两层,两层搞不定说明一定是哪里出问题了,一定是可以解决的,不能偷懒

缩小对象的声明周期,比如能定义在方法中就不要定义在方法外面。
避免创建过大的对象
避免同时加载大量数据

如果不能及时返回结果可以适当采用异步处理的方式等。

c++相关

c++中常见导致内存泄漏的原因:

  • new和malloc申请资源使用后,没有用delete和free释放;

  • 子类继承父类时,父类析构函数不是虚函数,所以子类对象地址空间得不到释放

  • 比如文件句柄、socket、自定义资源类没有使用对应的资源释放函数;

  • shared_ptr共享指针成环,造成循环引用计数,资源得不到释放。

减少内存泄漏的方法:
1、优先使用智能指针取代裸指针
2、利用RAII对象自动释放内存
3、手动new 的对象记得delete掉

真的OOM了怎么办?
1、 查看内存的使用情况
2、将malloc、free的调用信息记录到日志中,排查日志,看那里malloc但是没有free。

java相关

java中真的OOM了怎么办?
jmap -heap 查看是否内存分配过小,如果过小则调大一些即可

jmap -histo 查看是否有明显的对象分配过多且没有释放情况,可以看到占用内存最多的对象,但是如果是数组占用内存过多,我们不知道哪些对象持有这个数组,所以就需要 Dump 内存进行离线分析

jmap -dump 导出 JVM 当前内存快照,使用工具比如JProfiler进行分析快照

按照JVM规范,JAVA虚拟机在运行时会管理以下的内存区域:

程序计数器:当前线程执行的字节码的行号指示器,线程私有
JAVA虚拟机栈:Java方法执行的内存模型,每个Java方法的执行对应着一个栈帧的进栈和出栈的操作。
本地方法栈:类似“ JAVA虚拟机栈 ”,但是为native方法的运行提供内存环境。
JAVA堆:对象内存分配的地方,内存垃圾回收的主要区域,所有线程共享。可分为新生代,老生代。
方法区:用于存储已经被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。Hotspot中的“永久代”。
运行时常量池:方法区的一部分,存储常量信息,如各种字面量、符号引用等。
直接内存:并不是JVM运行时数据区的一部分, 可直接访问的内存, 比如NIO会用到这部分。

按照JVM规范,除了程序计数器不会抛出OOM外,其他各个内存区域都可能会抛出OOM。

最常见的OOM情况有以下三种:

  • java.lang.OutOfMemoryError: Java heap space ------>java堆内存溢出,此种情况最常见,一般由于内存泄露或者堆的大小设置不当引起。对于内存泄露,需要通过内存监控软件查找程序中的泄露代码,而堆大小可以通过虚拟机参数-Xms,-Xmx等修改。

  • java.lang.OutOfMemoryError: PermGen space ------>java永久代溢出,即方法区溢出了,一般出现于大量Class或者jsp页面,或者采用cglib等反射机制的情况,因为上述情况会产生大量的Class信息存储于方法区。此种情况可以通过更改方法区的大小来解决,使用类似-XX:PermSize=64m -XX:MaxPermSize=256m的形式修改。另外,过多的常量尤其是字符串也会导致方法区溢出。

  • java.lang.StackOverflowError ------> 不会抛OOM error,但也是比较常见的Java内存溢出。JAVA虚拟机栈溢出,一般是由于程序中存在死循环或者深度递归调用造成的,栈大小设置太小也会出现此种溢出。可以通过虚拟机参数-Xss来设置栈的大小。

java中OOM排查工具

1、jps:查看本机java进程信息。

2、jstack:打印线程的栈信息,制作线程dump文件。

3、jmap:打印内存映射,制作堆dump文件

4、jstat:性能监控工具

5、jhat:内存分析工具

6、jconsole:简易的可视化控制台

7、jvisualvm:功能强大的控制台

jps(JVM process Status)

jps可以查看虚拟机启动的所有进程、执行主类的全名、JVM启动参数
比如当执行了JPSTest类中的main方法后(main方法持续执行),执行 jps -l可看到下面的JPSTest类的pid为31354,加上-v参数还可以看到JVM启动参数。

31354 com.danny.test.code.jvm.JPSTest

jstat(JVM Statistics Monitoring Tool)

jstat查看堆信息

jstat -gc pid 500 10 :每500毫秒打印一次Java堆状况
(各个区的容量、使用容量、gc时间等信息),打印10

jmap(Memory Map for Java)

jmap查看堆内存信息

jmap -histo pid:打印出当前堆中所有每个类的实例数量和内存占用,
jmap -dump 可以转储堆内存快照到指定文件

jconsole

jconsole分析内存信息(各个区如Eden、Survivor、Old等内存变化情况)

JVM参数

堆内存相关:
-Xms:初始堆空间内存(默认为物理内存的1/64)
-Xmx:最大堆空间内存(默认为物理内存的1/4)
ms是memory start的简称,mx是memory max的简称

-Xmn:m 新生代内存大小为m

-XX:NewRatio:4
表示新生代:老年代=1:4

-XX:SurvivorRatio:8
表示Eden:S0:S1=8:1:1

OOM相关:
1、Dump异常快照以文件形式导出

-XX:+HeapDumpOnOutOfMemoryError JVM遇到OOM异常时能输出堆内信息
-XX:HeapDumpPath                设置堆内存溢出快照输出的文件地址

GC相关:

-verbose:gc 在控制台打印垃圾回收日志

-XX:+PrintGC 打印GC日志,内容简单
-XX:+PrintGCDetails 打印GC日志,内容详细
-XX:+PrintGCDateStamps 在GC日志中添加时间戳

-Xloggc:filename 指定gc日志路径

其他:
-verbose:class 在控制台打印类加载信息

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值