什么时候会发生内存泄漏:
(1)使用静态的集合类
public static List...
静态的集合类的生命周期和应用程序的生命周期一样长,所以在程序结束前容器中的对象不能被释放,会造成内存泄露。
解决办法是最好不使用静态的集合类,如果使用的话,在不需要容器时要将其赋值为null。
(2)单例模式可能会造成内存泄露
单例模式只允许应用程序存在一个实例对象,并且这个实例对象的生命周期和应用程序的生命周期一样长,如果单例对象中拥有另一个对象的引用的话,这个被引用的对象就不能被及时回收。
解决办法是单例对象中持有的其他对象使用弱引用,弱引用对象在GC线程工作时,其占用的内存会被回收掉。
(3)数据库、网络、输入输出流,这些资源没有显示的关闭
垃圾回收只负责内存回收,如果对象正在使用资源的话,Java虚拟机不能判断这些对象是不是正在进行操作,比如输入输出,也就不能回收这些对象占用的内存,所以在资源使用完后要调用close()方法关闭。
类的实例化顺序
(1) 类中的static代码在类加载时就执行,且只执行这一次。(先静态)
(2)如果一个类A继承了类B,在类A实例化时要先加载类B,在执行类A的构造函数时要先执行父类B的构造函数,所以此时要实例化类B。(先父后子)
(3)一个类实例化之前要先实例化类的成员变量(非静态),再执行该类的构造函数。(先成员后构造)
GC回收策略
JVM回收的对象,是那些已经不再被使用的对象。而判断是否不再被使用的原则,可以从两个方面来描述。第一是不可到达,第二是引用计数为0。这两点,其实说的是一回事,从不同的方面来描述罢了。
不可达检测
从GC Root出发,对象之间的引用链没有指向的对象,我们称之为不可到达。
引用计数为0
引用计数为0其实说的就是从GC Root开始的对象引用链到达该对象的引用计数为0,即没有可到达的引用路径指向它了。
在Java语言中,可以作为GC Roots的对象包括下面几种:
(1)虚拟机栈中引用的对象。
(2)方法区中静态属性引用的对象。
(3)方法区中常量引用的对象。
(4)本地方法栈中本地方法引用的对象。
四种类型的引用
强引用:使用最普遍的引用:Object o=new Object(); 特点:不会被GC。将对象的引用显示地置为null:o=null; (帮助垃圾收集器回收此对象)
软引用:用来描述一些还有用但是并非必须的对象,在Java中用java.lang.ref.SoftReference类来表示。对于软引用关联着的对象,只有在内存不足的时候JVM才会回收该对象。因此,这一点可以很好地用来解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。
弱引用:与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象(前提是此对象不再被其他外部对象引用),不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。如果这个对象是偶尔的使用,并且希望在使用时随时就能获取到,但又不想影响此对象的垃圾收集,那么你应该用 Weak Reference 来记住此对象。
ThreadLocalMap 使用弱引用的目的:当 threadLocal 实例可以被GC回收时,系统可以检测到该 threadLocal 对应的Entry是否已经过期(根据reference.get() == null来判断,如果为true则表示过期,程序内部称为stale slots)来自动做一些清除工作,否则如果不清除的话容易产生内存无法释放的问题——value对应的对象即使不再使用,但由于被threadLocalMap所引用导致无法被GC回收。
虚引用(幻影引用):一个对象是否有虚引用的存在完全不会对其生存时间构成影响,也无法通过虚引用来获取一个对象的真实引用。唯一的用处:能在对象被GC时收到系统通知,JAVA中用PhantomReference来实现虚引用。