最近小编准备换个工作,开开心心的打开腾讯会议,准备接受金蝶面试官的狂轰乱炸,面试中面试到一个Java内存溢出的面试题,小编自我感觉回答的不是很好,所以特别整理这篇博客。
内存溢出和数据库锁表的问题,可以说是程序员的噩梦,一般的程序异常,总是可以知道在什么时候或是在什么操作步骤上出现了异常,而且根据堆栈信息也很容易定位到程序中某处出现了问题。
内存溢出分析
内存溢出分析是指运用系统存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的的内存大于虚拟机能提供的最大内存。
为了解决Java中内存溢出问题,我们首先必须了解Java是如何管理内存的。Java的内存管理就是对象的分配和释放问题。在Java中,内存的分配是由程序完成的,而内存的释放是由垃圾收集器(Garbage Collection,GC)完成的,程序员不需要通过调用GC函数来释放内存,因为不同的JVM实现者可能使用不同的算法管理GC,有的是内存使用到达一定程度时,GC才开始工作,也有定时执行的,有的是中断式执行GC。但GC只能回收无用并且不再被其它对象引用的那些对象所占用的空间。Java的内存垃圾回收机制是从程序的主要运行对象开始检查引用链,当遍历一遍后发现没有被引用的孤立对象就作为垃圾回收。
引起内存溢出的原因常见的有以下几种:
- 内存中加载的数据量过于庞大,如一次性从数据库取出过多的数据
- 集合类中有对象的引用,使用完后未清空,使得JVM不能回收
- 代码中存在死循环或者递归产生过多的重复的对象实体
- 启动参数中设置的内存过小
解决办法
- 第一步:直接增加内存。
- 第二步:检查错误日志,查看“OutOfMemory”错误前是否有其他异常或错误。再根据具体的错误分析。比如说我司在出现
outOfMemory的前面发现生成兑换码的时候又出现异常,我们再去看这块代码。后面发现这里存在死循环,这个时候我们就可以去优化代码了。 - 第三步对代码进行分析
1, 检查代码 检查代码中是否有死循环或递归调用。
2,检查是否有大循环重复产生新对象实体。
3,检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
检查
4,检查集合类使用完后,未清除的问题。 - 使用内存工具动态查看内存使用情况。某个项目上线后,每次系统启动两天后,就会出现内存溢出的错误。这种情况一般是代码中出现了缓慢的内存泄漏,用上面三个步骤解决不了,这就需要使用内存查看工具了。
般来说,一个正常的系统在其启动完成后其内存的占用量是基本稳定的,而不应该是无限制的增长的。持续地观察系统运行时使用的内存的大小,可以看到在内存使用监控窗口中是基本规则的锯齿形的图线,如果内存的大小持续地增长,则说明系统存在内存泄漏问题。通过间隔一段时间取一次内存快照,然后对内存快照中对象的使用与引用等信息进行比对与分析,可以找出是哪个类的对象在泄漏。
通过以上四个步骤的分析与处理,基本能处理内存溢出的问题。当然,在这些过程中也需要相当的经验与敏感度,需要在实际的开发与调试过程中不断积累。
内存溢出类型
- java.lang.OutOfMemoryError: PermGen space
JVM 管理两种类型的内存,堆和非堆。堆是给开发人员用的上面说的就是,是在 JVM 启动时创建;非堆是留给 JVM 自己用的,用来存放类的信息的。它和堆不同,运行期内 GC 不会释放空间。如果 web app 用了大量的第三方 jar 或者应用有太多的 class 文件而恰好 MaxPermSize 设置较小,超出了也会导致这块内存的占用过多造成溢 - java.lang.OutOfMemoryError: Java heap space
第一种情况是个补充,主要存在问题就是出现在这个情况中。其默认空间 ( 即 -Xms) 是物理内存的 1/64 ,最大空间 (-Xmx) 是物理内存的 1/4 。如果内存剩余不到 40 %, JVM 就会增大堆到 Xmx 设置的值,内存剩余超过 70 %, JVM 就会减小堆到 Xms 设置的值。所以服务器的 Xmx 和 Xms 设置一般应该设置相同避免每次 GC 后都要调整虚拟机堆的大小。假设物理内存无限大,那么 JVM 内存的最大值跟操作系统有关,一般 32 位机是 1.5g 到 3g 之间,而 64 位的就不会有限制了。
注意:如果 Xms 超过了 Xmx 值,或者堆最大值和非堆最大值的总和超过了物理内存或者操作系统的最大限制都会引起服务器启动不起来。
不健壮的代码的特征及解决办法
-
尽早释放无用对象的引用。好的办法是使用临时变量的时候,让引用变量在退出活动域后,自动设置为 null
,暗示垃圾收集器来收集该对象,防止发生内存泄露。
对于仍然有指针指向的实例, jvm 就不会回收该资源 , 因为垃圾回收会将值为 null 的对象作为垃圾,提高 GC 回收机制效率; -
我们的程序里不可避免大量使用字符串处理,避免使用 String ,应大量使用 StringBuffer ,每一个 String
对象都得独立占用内存一块区域;String str = “aaa”;
String str2 = “bbb”;
String str3 = str + str2;// 假如执行此次之后 str ,str2 以后再不被调用 , 那它就会被放在内存中等待 Java 的 gc 去回收 , 程序内过多的出现这样的情况就会报上面的那个错误 , 建议在使用字符串时能使用 StringBuffer 就不要用 String, 这样可以省不少开销;
-
尽量少用静态变量,因为静态变量是全局的, GC 不会回收的;
-
避免集中创建对象尤其是大对象, JVM 会突然需要大量内存,这时必然会触发 GC
优化系统内存环境;显示的声明数组空间,而且申请数量还极大。 -
尽量运用对象池技术以提高系统性能;生命周期长的对象拥有生命周期短的对象时容易引发内存泄漏,例如大集合对象拥有大数据量的业务对象的时候,可以考虑分块进行处理,然后解决一块释放一块的策略。
-
不要在经常调用的方法中创建对象,尤其是忌讳在循环中创建对象。可以适当的使用 hashtable , vector
创建一组对象容器,然后从容器中去取那些对象,而不用每次 new 之后又丢弃 -
一般都是发生在开启大型文件或跟数据库一次拿了太多的数据,造成 Out Of Memory Error
的状况,这时就大概要计算一下数据量的最大值是多少,并且设定所需最小及最大的内存空间值。
Jvm内存设置多大合适呢:https://blog.csdn.net/weixin_30409927/article/details/106174303
引用:https://blog.csdn.net/u013521220/article/details/79523633