尚学堂 老师好!

上海:15201841284

广州:020-2989 6995

深圳:0755-23061965

武汉:027-8798 9193

西安:029-8822 8155

防止 Java 内存泄露的安全措施

Java 通过 GC(一个守护线程)隐性回收内存资源。GC 会定期检查有没有哪个对象是不可达的,准确来说,没有引用指向这个对象。如果有,GC 就会回收这块内存。

更多精彩内容以及学习资料,尚学堂论坛bbs.bjsxt.com免费下载。

现在的问题是我们应不应该担心内存泄漏问题或者说 Java 怎么处理这个问题的?

关注一下定义:一个对象只有在无可到达(无法使用)且没有任何现有线程可以使用到它时才会被垃圾回收器回收。

所以如果一个对象在应用中没有使用但无意中被引用了,那它是不会被垃圾回收器回收的,也就存在内存泄漏的危险了。

GC关注这些不可达的对象,但是不能判断对象是否不再使用,对于应用中存在可能使用被回收的对象的情形,程序员应该格外注意相应的业务逻辑代码。可笑的小错误将会成为大问题。

内存泄漏会在很多情形下产生,让我们看看下面的例子:

例子 1:自动装箱

 package com.example.memoryleak; public class Adder {        public long addIncremental(long l)        {               Long sum=0L;                sum =sum+l;                return sum;        }        public static void main(String[] args) {               Adder adder = new Adder();               for(long i;i<1000;i++)               {                      adder.addIncremental(i);               }        } }

你能指出其中的内存泄漏么?

这儿的代码有个小问题,我没有使用基本数据类型long,而是使用了封装类Long,正是如此,这儿会造成内存泄漏。因为自动装箱机制,sum=sum+l;这句在每一次循环中将会创建一个新的Long对象,因此创建了有1000个无用的对象。请避免混合使用封装数据类型和基础数据类型,如果可以,尽量使用基础数据类型。

例子2: 使用缓存

 package com.example.memoryleak; import java.util.HashMap; import java.util.Map; public class Cache {        private Map<String,String> map= new HashMap<String,String>();        public void initCache()        {               map.put("Anil", "Work as Engineer");               map.put("Shamik", "Work as Java Engineer");               map.put("Ram", "Work as Doctor");        }        public Map<String,String> getCache()        {               return map;        }        public void forEachDisplay()        {               for(String key : map.keySet())               {                 String val = map.get(key);                                  System.out.println(key + " :: "+ val);               }        }        public static void main(String[] args) {                           Cache cache = new Cache();               cache.initCache();               cache.forEachDisplay();        } }

在这里,发生内存泄漏是由于内部map数据结构。这个类是从缓存中获取显示员工的职位。 一旦它们显示完成,就不必把这些元素存储在缓存中了。

我们忘了清除缓存,尽管程序不在需要缓存中的对象,但它们不能被GC,因为map持有它们的强引用。

因此,但你使用缓存时,缓存中的对象不再需要时记得清楚它们。 另外,,你应该使用 WeakHashMap来初始化缓存。 使用WeakHashMap的好处时,一旦key不被其它对象引用,它就可以被GC回收。

WeakHashMap有许多值得讨论的地方,我将再写一篇文章介绍。务必小心使用WeakHashMap,如果你需要重新使用缓存中的数据,可能发生这样的情况:该数据可能不再被其他对象引用,那么引用可能被回收,他的值也会消失。

例子 3:关闭连接

 try {   Connection con = DriverManager.getConnection();   …………………   con.close(); }  Catch(Exception ex) { }

在上面的例子中,我们在try代码块中关闭了(昂贵的)连接资源,因此如果一旦发生异常,close语句跳过执行,那么连接将不会被关闭。这样,这个连接不会再返回连接池,就引发了一次内存泄漏。

请养成将关闭语句写在finally代码块中的习惯。

例子4: 使用构造方法

 package com.example.memoryleak; import java.util.HashMap; import java.util.Map; public class CustomKey {        public CustomKey(String name)        {               this.name=name;        }        private String name;        public static void main(String[] args) {               MapString> map = new HashMapString>();               map.put(new CustomKey("Shamik"), "Shamik Mitra");               String val = map.get(new CustomKey("Shamik"));               System.out.println("Missing equals and hascode so value is not accessible from Map " + val);        } }

就像在CustomKey中我们忘了提供equals() 和 hashcode() 的实现, 因此,存储在map中的一个键和值都不能被检索, 因为map 的get()方法检查 hashcode() 和 equals(). 但是这个元素不会被GC掉, 因为map有引用它,但应用程序却无法访问它.显示它是内存泄漏.

因些创建自己的Custom key, 总是需要提供 equals 和 hashcode() 实现.

例子 5: 可变的 Custom Key

 package com.example.memoryleak; import java.util.HashMap; import java.util.Map; public class MutableCustomKey {        public MutableCustomKey(String name)        {               this.name=name;        }        private String name;        public String getName() {               return name;        }        publicvoid setName(String name) {               this.name = name;        }        @Override        public int hashCode() {               final int prime = 31;               int result = 1;               result = prime * result + ((name == null) ? 0 : name.hashCode());               return result;        }        @Override        public boolean equals(Object obj) {               if (this == obj)                      return true;               if (obj == null)                      return false;               if (getClass() != obj.getClass())                      return false;               MutableCustomKey other = (MutableCustomKey) obj;               if (name == null) {                      if (other.name != null)                            return  false;               } elseif (!name.equals(other.name))                      return false;               return true;        }        public static void main(String[] args) {               MutableCustomKey key = new MutableCustomKey("Shamik");                            Map map = new HashMap();               map.put(key, "Shamik Mitra");               MutableCustomKey refKey = new MutableCustomKey("Shamik");               String val = map.get(refKey);               System.out.println("Value Found " + val);               key.setName("Bubun");               String val1 = map.get(refKey);               System.out.println("Due to MutableKey value not found " + val1);        } }

尽管在这里我们为custom Key提供了 equals() 和 hashcode(), 将它保存在map中后我们没有刻意的改变它.如果它的属性被改变,那么这个元素将永远不会被应用程序找到,但是map持有它的一个引用,因此这里会发生内存泄漏.

总是让你的 custom key 不可变.

例子 6: 内部数据结构

 package com.example.memoryleak; public class Stack {        privateint maxSize;        privateint[] stackArray;        privateint pointer;        public Stack(int s) {               maxSize = s;               stackArray = newint[maxSize];               pointer = -1;        }        public void push(int j) {               stackArray[++pointer] = j;        }        public int pop() {               return stackArray[pointer--];        }        public int peek() {               return stackArray[pointer];        }        publicboolean isEmpty() {               return (pointer == -1);        }        public boolean isFull() {               return (pointer == maxSize - 1);        }        public static void main(String[] args) {               Stack stack = new Stack(1000);               for(int ;i<1000;i++)               {                      stack.push(i);               }               for(int ;i<1000;i++)               {                      int element = stack.pop();                      System.out.println("Poped element is "+ element);               }        } }

在这里,当栈第一次增长后接着收缩时我们面临一个棘手的问题。实际上由于内部实现,栈内部有一个数组,对外部程序来说,栈的活动部分是指向该数组的指针。

所以当堆栈增长到1000时,内部的数组也就被元素填满了,但后面当我们弹出所有元素后,指针指到了0,对应用程序来说它也变为空的了,但是内部的数组却还包含被弹出对象的引用。在Java中,我们叫它过期引用。过期引用是不能再次被引用的引用。

这个引用不能被回收,因为数组中还保留着被弹出后就不需要的元素。

更多精彩内容以及学习资料,尚学堂论坛bbs.bjsxt.com免费下载。

为了解决这个问题,我们需要在弹出操作中把值设为null,这样这些对象才能被回收。

 public int pop() {               int size = pointer--               int element= stackArray[size];               stackArray[size];               return element;        }

防止内存泄露的安全措施:

防止 Java 内存泄露的安全措施

  • 北京校区
  • 西安校区
  • 山西校区
  • 武汉校区
  • 深圳校区
  • 上海校区
  • 广州校区
  • 保定招生办
  • 黑龙江项目办

北京京南校区:北京亦庄经济开发区科创十四街6号院1号楼 赛蒂国际工业园
咨询电话:400-009-1906 / 010-56233821
面授课程: JavaEE+微服务+大数据     大数据+机器学习+平台架构     Python+数据分析+机器学习  人工智能+模式识别+强化学习   WEB前端+移动端+服务端渲染

地址:陕西省西安市高新区西安软件园西区创新信息大厦A座三层尚学堂

电话:029-88228155 / 18291433445

山西学区地址:山西省晋中市榆次区大学城大学生活广场万科商业A1座702

武汉学区地址:武汉市东湖高新区光谷金融港B22栋11楼
咨询电话:027-87989193

深圳校区地址:深圳市宝安区航城大道U8智造产业园U6栋3楼
咨询电话:0755-23061965 / 18898413781

上海尚学堂校区地址:上海市浦东新区城丰路650号
咨询电话:021-67690939

广州校区地址:广州市天河区车陂街道大岗路5号中侨广场2栋321室(四号线车陂站D出口,或brt车陂站)
咨询电话:18948349646

保定招生办公室

地址:河北省保定市竞秀区朝阳南大街777号鸿悦国际1101室

电话:15132423123

黑龙江项目办
地点:哈尔滨市松北区博文路青年部落孵化器1层
电话:15321415678
Copyright 2006-2021 北京尚学堂科技有限公司  京ICP备13018289号-19  京公网安备11010802015183   营业执照
网站维护:北京尚学堂科技有限公司昌平分公司