我们的web应用跑在WebSphere或者tomcat应用服务器下,经常会出现一个内存溢出的错误,使服务没法访问,这时候,为了不影响业务,服务能马上恢复正常,一般采取的方式是调整应用服务器的Java虚拟机内存大小,加大内存,如果内存已经达到最大,显然这种方式行不通,第二种方式就是重启应用服务器,目的是释放Java虚拟机的内存。这两种方式虽然可以马上解决服务不能访问的问题,但是是治标不治本的,如果内存溢出是由于内存泄露引起的,我们需要在程序中找出引起内存泄露的原因和代码。所谓内存泄露,就是内存越来越大,但是不知道内存中存的都是些什么东西,找不到他的索引。
针对内存泄露,有这种可能原因。我们在编写Java程序的时候,因为Java的特性,我们需要不停的创建对象,然后就去使用这些对象,不关心最终不用的时候这些对象是怎么被回收的。因为Java有个垃圾自动回收机制,这个确实不用我们关心,垃圾回收会自动监听对象的生命周期,如果发现对象不再使用,他就回收这个对象,释放此对象占用的内存。按理说,所有内存的对像都能被释放,但为什么还会产生内存泄露呢?有没有这种可能,当垃圾回收拿着对象的索引去内存中找这个对象的时候,发现是空,那么这个索引对应的那个对象怎么会凭空消失,他还在内存中吗?答案是肯定的,还在。就是找不到。
我们经常用哈希集合存储对象,哈希集合有个特点,里面存储的对象是按块存储的,一块存储多个对象,每一个块都有一个唯一的哈希值。当程序检索这些对象的时候,他首先用哈希值定位到块,然后再在块里面找。这样我们在比较哈希集合中的两个对象是否相等的时候,需要两步,首先需要重写对象的hashcode,返回一个哈希值,如果哈希值一样,那比较的第一步说明是相等的,第二部当然就是equals方法了。因为哈希集合中的对象首先是通过哈希值检索的,如果我们在重写hashcode方法的时候,使用了一些变量,比如对象的某个属性值,这个属性值随着程序的执行会变化的,那么初次产生的哈希值和最终产生的哈希值是不一样的。拿最终产生的哈希值去检索这个对象,那就找不到了,那这个初次产生的哈希值对应的对象永远就存储的内存中了,只要这段代码执行一遍,就会产生一个遗漏的对象,时间一久,内存就会越来越大,最终溢出。
就像居民的身份证号码一样,合格的居民都有一个号码和他对应着,如果没有这个号码,或者之前的号码被人为的修改了,那么这个人国家就检索不到,人口没法控制,就会越来越多。
网友评论