内存中加载的数据量过于庞大,如一次从数据库取出过多数据;集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;代码中存在死循环或循环产生过多重复的对象实体;使用的第三方软件中的BUG;启动参数内存值设定的过小;
检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
检查代码中是否有死循环或递归调用。
1. 对象。A.创建。首先检查指令的参数能不能在常量区找到类的符号引用,并检查这个类是否加载、解析和初始化过,如果没有就执行类的加载过程。其次是内存分配,类加载之后就知道要分配的内存大小,分配方法有两种,一种是指针碰撞,就是一块内存是使用过的,一块是未使用的,用一个指针分割,新分配的内存指针就向空闲的挪动,compact功能的虚拟机是用指针碰撞;另一种是空闲列表,就是一个列表记录空闲的内存块,不断更新列表,新分配的内存在列表中寻找一个合适大小的内存块,sweep功能的虚拟机是使用空闲列表。第三,在分配内存空间的时候,还要考虑并发性。有两个方法,一种是同步处理,如采用CAS和失败重试的方法;另外一种是把内存分配动作按照线程划分在不同的空间之中,每个线程在堆中预先分配一小块内存,本地线程分配缓冲TLAB,那个线程需要分配内存在那个TLAB上分配,只有TLAB用完了,才要同步锁定,重新分配。第四、对对象进行必要设置,比方说对象属于那个类,如何找到类的元数据信息和对象hashcode以及对象GC分代年龄等。
B.对象的内存布局。分为对象头、实例数据和对齐填充。对象头包括两部分,第一部分是存储对象自身信息,如hashcode,GC分代年龄,锁状态等;第二部分是类型指针,对象指向它的类的元数据的指针,虚拟机通过这个指针确定这是那个类的实例。
C.对象访问定位。两种方式,一种是句柄访问,句柄池有访问对象实例数据的指针和访问对象数据类型的指针。这个访问最大好处是reference是稳定的句柄池地址,对象改变都是改变句柄池里面的指针,而reference本身不动。另外一种就是直接指针,它有到对象类型数据的指针和实例数据。这个访问的好处是速度更快,节省了一次指针定位的开销。
2. 内存溢出OOM。
A.堆溢出。堆存放的是对象实例,只要不断创建对象,并且保证GC Root到对象有可大路径避免被垃圾回收清除掉对象,那么对象数量达到最大堆容量限制就会OOM。用内存映象分析工具,Eclipse Memory Analyzer分析一下。
B.虚拟机栈和本地方法栈溢出。分为两种,一种是如果线程请求的栈深度大于虚拟机所允许的最大深度,抛出StackOverFlowError异常;另一种是如果虚拟机在扩展栈时无法申请到足够内存空间,抛出OutOfMemoryError异常。可以减小最大堆和栈容量来获取更多的线程数量。
C.方法区和常量池溢出。会有额外提示 PermGen space。
D.本机直接内存溢出。这个Heap Dump文件看不到内存占用,但是如果有直接或简介使用了NIO,那有可能就是本机直接内存溢出了。
先检查, 你的程序里面的数据库连接是否使用后都关闭。有个方法, 虽然有点笨, 但简单有效。
用记事本写一个bat文件。 内容如下:
net stop "World Wide Web Publishing Service"
net Stop "HTTP SSL"
net Stop "MSSQLSERVER"
net start "World Wide Web Publishing Service"
net start "HTTP SSL"
net start "MSSQLSERVER"
然后加入计划任务里, 让它每天重启一下。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)