文章目录 [隐藏]
先看下2个概念的定义:
1)内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。
2)内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
memory leak会最终会导致out of memory。
1.因为集合仍持有对象的引用引起的OOM
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
public class TestOutOfMemoryError_1 { /** * @Author:cuiweiyou.com * 因为集合仍持有对象的引用引起的OOM */ public static void main(String[] args) { ArrayList<Person> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { Person p = new Person("小白" + i, i); list.add(p); p = null;// list中仍然持有p堆内存的引用 System.out.println(p = null); System.out.println(list.get(list.size() - 1).name);// 内存泄露就是指一个不再被程序使用的对象或变量一直被占据在内存中 } list = null; // 此时GC才可以回收内存,挽回内存泄露 // 如果这个list是成员变量,生命周期是由类实例决定的,内存泄露风险更大; // 如果只在局部使用的变量,不要声明为成员变量; // 如果这个list是static类型的成员变量,更更大 } } class Person { public Person() { } public Person(String string, int i) { this.name = string; this.age = i; } public String name; public int age; } |
2.因为集合自身限制引起的OOM
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
public class TestOutOfMemoryError_2 { /** * @Author:cuiweiyou.com * 因为集合自身限制引起的OOM */ public static void main(String[] args) { ArrayList<Person2> list = new ArrayList<>(); // 最大容量即Integer.MAX_VALUE - 8,如果数据量过大OutOfMemoryError:Java heap space。超出堆空间 for (int i = 0; i < Integer.MAX_VALUE - 9; i++) { list.add(new Person2("小白" + i, i)); // OutOfMemoryError: GC overhead limit exceeded。数据量在可容范围,但GC占用了太多资源 System.out.println("当前容量:" + list.size()); } System.out.println("----------cuiweiyou.com-------------"); } } class Person2 { public Person2() { } public Person2(String string, int i) { this.name = string; this.age = i; } public String name; public int age; } |
3.因为集合中对象的修改引起的OOM
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
public class TestOutOfMemoryError_3 { /** * @Author:cuiweiyou.com * 因为集合中对象的修改引起的OOM */ public static void main(String[] args) { ArrayList<Person3> list = new ArrayList<>(); for (int i = 0; i < 3; i++) { Person3 p = new Person3("小白" + i, i); list.add(p); p.name = "小黑" + i; list.remove(p); // 因为类的equals方法被重写,导致remove可能失败,会诱发OOM } for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } } } class Person3 { @Override public boolean equals(Object obj) { // if (this == obj) // return true; // if (obj == null) // return false; // if (getClass() != obj.getClass()) // return false; return false; //return super.equals(obj); } public Person3() { } public Person3(String string, int i) { this.name = string; this.age = i; } public String name; public int age; @Override public String toString() { return "姓名:" + name + ",年龄:" + age; } } |
4.因为IO引起的OOM
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
public class TestOutOfMemoryError_4 { /** * @Author:cuiweiyou.com * 因为IO引起的OOM <br/> * 数据库连接、 <br/> * 网络连接、<br/> * 文件操作、 <br/> * 需手动干涉的第三方框架(spring中的session), <br/> * 调用其close()方法(或类似方法)将其连接关闭 */ public static void main(String[] args) { FileInputStream fis = null; try { fis = new FileInputStream(new File("")); } catch (Exception e) { e.printStackTrace(); } finally { if (null != fis) try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } } |
5.单例模式引起的OOM
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
public class TestOutOfMemoryError_5 { /** * @Author:cuiweiyou.com * 单例模式引起的OOM */ public static void main(String[] args) { SingleClass sc = SingleClass.getInstance(); // 单利 Name n = new Name("小芳"); sc.setName(n); n = null; // 小芳的引用还存在,可能会OOM System.out.println(sc.getName().name); // 仍能在内存中获取“小芳” sc.setName(null); //System.out.println(sc.getName().name); } } class SingleClass { private static SingleClass instance; private Name name; private SingleClass(){ } public static SingleClass getInstance(){ if(null == instance){ instance = new SingleClass(); } return instance; } public void setName(Name name){ this.name = name; } public Name getName() { return name; } } class Name{ public Name(String str) { this.name = str; } public String name; } |
6.内部类持有外部类引用,导致的OOM
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
public class TestOutOfMemoryError_6 { class NormalInnerClass { // 内部类,会诱发OOM } static class StaticInnerClass { // 静态内部类(嵌套类),避免OOM } /** * @Author:cuiweiyou.com * 内部类持有外部类引用,导致的OOM<br/> * 通过反射可以验证,<br/> * 通过反编译可以更直观的验证,http://blog.csdn.net/qq_22706515/article/details/51321718 */ public static void main(String[] args) { testNormalInnerClass(); testStaticInnerClass(); } private static void testNormalInnerClass(){ //NormalInnerClass inner = new NormalInnerClass(); NormalInnerClass inner = new TestOutOfMemoryError_6().new NormalInnerClass(); // 反射得到构造函数 Constructor<NormalInnerClass>[] constructors = (Constructor<NormalInnerClass>[]) inner.getClass().getDeclaredConstructors(); for (int i = 0; i < constructors.length; i++) { Constructor<NormalInnerClass> c1 = constructors[i]; c1.setAccessible(true); // 反射构造函数的参数的类型 Class[] types = c1.getParameterTypes(); for (int j = 0; j < types.length; j++) { System.out.println("n内部类引用的外部类:" + types[j].getName()); // pkg.TestOutOfMemoryError_6 } } inner = null; // 此时虽然inner变量为null了,但其引用的外部类还是存活的,所以原inner指向的堆内存中的“实例”并不能被回收 } private static void testStaticInnerClass(){ StaticInnerClass sic = new TestOutOfMemoryError_6.StaticInnerClass(); Constructor<NormalInnerClass>[] constructors = (Constructor<NormalInnerClass>[]) sic.getClass().getDeclaredConstructors(); for (int i = 0; i < constructors.length; i++) { // 1,默认无参的 Constructor<NormalInnerClass> c1 = constructors[i]; c1.setAccessible(true); Class[] types = c1.getParameterTypes(); for (int j = 0; j < types.length; j++) { System.out.println("s内部类引用的外部类:" + types[j].getName()); // 不会执行这一句 } } sic = null; // 静态内部类不依赖于外部类的实例,其被null后,相应的内存即可回收 // https://www.zhihu.com/question/20969764 } } |
7.内存优化
1)规避以上可能引发OOM的情况;
2)通过软引用和弱引用优化;
3)优化算法避免重复操作;
4)StringBuilder替代String的连接;
5)严格全局变量和局部变量的定义;
6)避免使用包装类;
7)尝试“池”操作;
声明
本文由崔维友 威格灵 cuiweiyou vigiles cuiweiyou 原创,转载请注明出处:http://www.gaohaiyan.com/2184.html
承接App定制、企业web站点、办公系统软件 设计开发,外包项目,毕设