目录
扫描二维码关注公众号,回复:
16728454 查看本文章
1、内存泄漏与内存溢出
内存泄漏与内存溢出的区别:
- 内存泄漏:不再被使用的对象占用的内存空间,本应该被释放,但没有被垃圾回收掉。
- 内存溢出:在程序运行中,无法申请到足够的内存资源。
1、内存泄漏
产生原因:
- 使用静态的集合类。静态变量不会被垃圾回收,而集合占用的内存又一般很大。
- 各种连接没有及时关闭,比如数据库连接、IO连接
- 一些强引用的对象,在不使用后没有置为null,导致无法被回收
- 变量的作用域设置不合理,存活周期过长
- 过多的单例模式类
解决方案:
- 避免在循环中创建对象,尽量复用对象
- 及时释放无用的对象引用
- 少用静态集合
- 及时关闭连接
- 对于String的操作,使用StringBuilder或者StringBuffer,不要直接拼接字符串
2、内存溢出
内存溢出的情况:
- 虚拟机栈和本地方法栈溢出
- 如果线程请求的栈深度,大于虚拟机允许的栈深度,抛出StackOverflowError
- 如果虚拟机在扩展栈时,无法申请到足够的内存空间,抛出OutOfMemoryError
- 堆溢出
- 遇到堆溢出,必须先判断,到底是发生了内存泄漏还是内存溢出
- 如果是内存泄漏,通过工具查看泄漏对象到 GC Roots 的引用链,找到泄漏对象无法被垃圾收集器自动回收的原因。
- 如果不是内存泄漏,而且代码看不出问题,就考虑增大虚拟机的内存参数(-Xmx 与-Xms)
- 方法区溢出
- 考虑修改方法区占用的内存大小
- 运行时常量池溢出
内存溢出的原因:
- 内存中加载的数据过于庞大
- 代码中存在死循环,或者存在大量对象的创建
- 虚拟机内存参数设置过小
内存溢出的解决方案:
- 修改JVM内存参数。一般将-Xms 和-Xmx 设置成相同的值,避免每次GC后调整堆的大小。堆一般设置为物理内存的80%
- 检查错误日志,查看内存溢出背后的实际原因
- 对代码进行走查和分析,排查产生错误的代码
- 使用内存查看工具,动态地监视内存的使用情况,尝试复现内存溢出的场景,进而发现问题。
2、提高垃圾回收效率的方法
- 对象不再使用时,及时将它显式置为null,方便垃圾收集器快速判断
- 少用 System.gc(),因为它会触发Full GC
- 少用静态变量。静态变量会作为GC Roots,永远不会回收。
- 对于String的操作,使用StringBuilder或者StringBuffer,不要直接拼接字符串。因为会在堆上产生很多额外的字符串对象
- 分散对象创建和销毁的时机,不要突然创建或销毁大量对象,因为这样可能会触发Full GC
- 少用finalize()方法,会加重垃圾回收的负担
- 善用弱引用与软引用类型,不要全部使用强引用
- 能用基本数据类型,就不要使用包装类型。因为包装类型占用了更大的内存
- 增大堆内存,可以减少垃圾回收的频率。
3、常用的命令
4、常用的JVM参数
1、设置堆内存大小
-Xms<heap size>[unit]
-Xmx<heap size>[unit]
- heap size:内存大小
- unit:单位,比如k、m、g
比如要设置最小为2G,最大为5G:
-Xms2g -Xmx5g
2、设置新生代内存大小
默认新生代的内存是1G左右,最大无上限
有两种设置新生代内存大小的方式:
指定最小值和最大值
-XX:NewSize=<young size>[unit]
-XX:MaxNewSize=<young size>[unit]
规定新生代内存为一个固定值
-Xmn256m
设置新生代与老年代的比值
-XX:NewRatio=1
- 含义是,整个新生代(包括Eden、From Survivor、To Survivor)和老年代的内存占比
3、设置元空间内存大小
由于元空间是在直接内存上的,而直接内存对应着宿主机物理内存。
所以如果不对元空间的大小做限制,随着运行创建了大量的类,有可能会耗尽宿主机的所有内存。
设置元空间的最小值和最大值
-XX:MetaspaceSize=N
-XX:MaxMetaspaceSize=N
4、设置使用哪种垃圾回收器
-XX:+UseSerialGC
-XX:+UseParallelGC
-XX:+UseParNewGC
-XX:+UseG1GC
5、记录垃圾回收日志
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=< number of log files >
-XX:GCLogFileSize=< file size >[ unit ]
-Xloggc:/path/to/gc.log