java内存泄漏-java栈内存和堆内存习题
内存泄漏的定义:一个不再被程序使用的对象或变量,仍然占据着内存中的存储空间。 一次内存泄漏看似影响不大,但是内存泄漏累积起来的后果就是内存溢出。
Memory overflow out of memory:当程序申请内存时,没有足够的内存给申请者,或者说,给了你一个存储int类型数据的存储空间,但是你存储了long类型数据,那么结果就是insufficient memory 如果使用了,此时会报错OOM,也就是所谓的内存溢出。
两者的关系:
内存泄漏的积累最终会导致内存溢出。 内存溢出就是你想要的内存空间超过了系统实际分配给你的空间。 这时候系统就相当于不能满足你的需求,就会报内存溢出错误。 内存泄漏是指你向系统申请分配内存以供使用(new),但使用后不归还(delete)。 丢失),系统无法将其重新分配给需要它的程序。 相当于用钥匙租柜子。 放完东西锁上柜子后,钥匙丢了或钥匙没归还。 那么结果就是这个柜子谁也用不了,也不能扔。 回收商回收了,因为它找不到任何关于它的信息。 内存溢出:一个盘子通过各种方法只能放4个水果,你放了5个水果,结果掉在地上不能吃。 这是溢出。 比如栈满了,压入栈中java内存泄漏,势必会造成空间溢出,称为溢出。 即分配的内存不足以放下数据项序列,称为内存溢出。 说白了就是受不了这么多,所以会报错,
由于java的JVM引入了垃圾回收机制,垃圾回收器会自动回收不再使用的对象。 了解JVM回收机制的人都知道,JVM通过引用计数和可达性分析算法来判断对象是否不再被使用。 对象的本质就是判断一个对象是否还被引用。 那么在这种情况下java内存泄漏,由于代码的实现方式不同,就会出现很多种内存泄漏问题(使JVM误认为对象还在引用中,无法回收,从而造成内存泄漏)。
1、静态集合类,如HashMap、LinkedList等。如果这些容器是静态的,那么它们的生命周期与程序是一致的,程序结束前不会释放容器中的对象,从而造成内存泄漏。 简而言之,长寿命对象持有对短寿命对象的引用。 尽管不再使用短寿命对象,但它们不能被回收,因为长寿命对象持有对它们的引用。
2、各种连接,如数据库连接、网络连接、IO连接等。 在操作数据库的过程中,首先要与数据库建立连接,当不再使用时,需要调用close方法释放与数据库的连接。 只有在连接关闭后,垃圾收集器才会回收相应的对象。 否则,如果在访问数据库的过程中没有显式关闭Connection、Statement或ResultSet,会导致大量对象得不到回收,从而导致内存泄漏。
3.变量范围不合理。 一般来说,一个变量的定义范围大于它的使用范围,很可能会造成内存泄漏。 另一方面,如果对象没有及时设置为null,则很可能造成内存泄漏。
public class UsingRandom { private String msg; public void receiveMsg(){ readFromNet();// 从网络中接受数据保存到msg中 saveDB();// 把msg保存到数据库中 } }
如上面的伪代码,通过readFromNet方法将接收到的消息保存在变量msg中,然后调用saveDB方法将msg的内容保存到数据库中。 这时候msg就没有用了,因为msg的生命周期和对象的生命周期是一样的,此时msg是无法回收的,从而造成内存泄漏。
其实这个msg变量可以放在receiveMsg方法里面。 当method用完,msg的生命周期就会结束,此时可以回收。 还有一种方法是在使用msg后将msg设置为null,这样垃圾回收器也会回收msg的内存空间。
4.内部类持有外部类。 如果外部类的实例对象的方法返回了内部类的实例对象,长期引用内部类对象,即使不再使用外部类实例对象,但由于内部类持有一个外部类的实例对象,这个外部类对象不会被垃圾回收,同样会造成内存泄漏。
5.更改散列值。 对象存储到HashSet集合后,对象中参与哈希值计算的字段不能修改。 否则,对象修改后的哈希值与原来存储在HashSet集合中的哈希值相同。 在这种情况下,即使contains方法使用对象的当前引用作为参数在HashSet集合中检索对象,也会返回找不到对象的结果,同样会导致当前对象无法删除仅来自 HashSet 集合,导致内存泄漏。
专注于
感谢阅读,如果本文对您有帮助,请点赞、收藏、关注、转发。 您的帮助是我们前进的动力,我们将为您提供更多有价值的内容……谢谢!