java的内存泄露是怎么回事

java的底层是C++编写的,与C++不同,Java对内存对象的访问,使用的是引用方式,再Java程序汇总,这个引用变量本身既可以存放堆内存中,又可以放在代码栈的内存中(与基本数据类型相同)
当一个内存对象失去了所有引用之后,GC就可以将其回收,放过来说,如果这个对象还存在引用,那么它将不会被GC回收,那怕是JAVA虚拟机抛出OutofMErmoryError


1 Vector v=new Vector(10);
2 for (int i=1;i<100; i++){
3 Object o=new Object();
4 v.add(o);
5 o=null;
6 }

上述代码,就算,对象o被置为空,但是gc再追踪代码栈的引用时,会发现V引用,再继续追踪下去,会发现v引用指向的内存空间,又存在指向OBject的引用。

1、在可达性分析法中,不可达的会被清理掉,但有些对象是无用的不会再被调用,这些对象就是java的内存泄露。

2、生命周期有长有短,如果长声明周期的对象,持有短生命周期的引用就可能户出现内存泄漏。

可达性分析法,从根节点(GC Roots)对象出发进行搜索,如果再根节点和对象之间没有一条可达的路径,则称该对象是不可达的。

内存泄漏情况

1、静态集合类 HashMap,LinkedList等等,如果这些容器为静态的,那么他们的生命周期与程序一致,则容器中的对象再程序结束之前将不能被释放,从而造成内存泄漏,简单而言,长生命周期的对象短生命周期的引用,尽管短生命周期的对象 不再使用,但是i因为 长生命周期对象持有他的引用而导致不能被回收

2、各种链接,数据库、网络、IO链接,访问数据库过程中,对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方法内部,当方法使用完,那么msg的生命周期也就结束,还有一种就是使用完变量,把msg设置为null

4、内部类持有外部类,如果一个外部类的实例对象的方法返回了一个内部类的实例对象,即使那个外部类实例对象不在被使用,但由于内部类持有外部类的实例对象那个,这个外部类对象将不会被垃圾回收

5、改变哈希值当一个对象被存储进HashSet集合种以后,就不能改变这个对象中的那些参数计算哈希值的字段了,否则,对象修改后的哈希值,与最初存储进Hash集合种时的哈希值就不同了,即使用contains方法去检索,也检索不出来。

6、栈为置为null

import java.sql.Array;
import java.util.Arrays;
public class Stack {
    private Object[] elements;
    private int size=0;
    private static final int DEFAULT_INITIAL_CAPACITY=16;
    public Stack(){
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }
    public void push(Object e){
        ensureCapacity();
        elements[size++]=e;
    }
    private void ensureCapacity() {
            if(elements.length==size){
                elements= Arrays.copyOf(elements,2*size+1); 
        }
    }
    public Object pop(){
        if (size==0){
            return elements[--size];
        }
    }}
    /*Arrays.copyOf(array, to_index);
Arrays.fill(array, from_index, to_index);
第一个方法其实就是返回一个数组,而这个数组就等于数组array的前to_index个数,也就是array[0] ~ array[to_index - 1]。
而第二种方法也只是加了一个初始的位置,即返回一个数组等于array[from_index] ~ array[to_index - 1]。
*/

如果栈先增长,在收缩,那么从栈中弹出的对象将不会被当作垃圾回收,即使程序不再使用栈中的这些队象,他们也不会回收,因为栈中仍然保存这对象的引用,俗称过期引用,这个内存泄露很隐蔽。
解决方案

public Object pop() {
    if (size == 0)
    throw new EmptyStackException();
    Object result = elements[--size];
    elements[size] = null;
    return result;
}

7、缓存泄漏
把对象放入缓存后很容易遗忘,对于这个问题,可以使用WeakhashMap代表缓存,此map的特点是,除了自身有对key的引用外,没有其他引用,次map就会丢弃值

package com.ratel.test;

/**
 * @业务描述:
 * @package_name: com.ratel.test
 * @project_name: ssm
 * @author: ratelfu@qq.com
 * @create_time: 2019-04-18 20:20
 * @copyright (c) ratelfu 版权所有
 */
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.TimeUnit;

public class MapTest {
    static Map wMap = new WeakHashMap();
    static Map map = new HashMap();
    public static void main(String[] args) {
        init();
        testWeakHashMap();
        testHashMap();
    }



    public static void init(){
        String ref1= new String("obejct1");
        String ref2 = new String("obejct2");
        String ref3 = new String ("obejct3");
        String ref4 = new String ("obejct4");
        wMap.put(ref1, "chaheObject1");
        wMap.put(ref2, "chaheObject2");
        map.put(ref3, "chaheObject3");
        map.put(ref4, "chaheObject4");
        System.out.println("String引用ref1,ref2,ref3,ref4 消失");

    }
    public static void testWeakHashMap(){

        System.out.println("WeakHashMap GC之前");
        for (Object o : wMap.entrySet()) {
            System.out.println(o);
        }
        try {
            System.gc();
            TimeUnit.SECONDS.sleep(20);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("WeakHashMap GC之后");
        for (Object o : wMap.entrySet()) {
            System.out.println(o);
        }
    }
    public static void testHashMap(){
        System.out.println("HashMap GC之前");
        for (Object o : map.entrySet()) {
            System.out.println(o);
        }
        try {
            System.gc();
            TimeUnit.SECONDS.sleep(20);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("HashMap GC之后");
        for (Object o : map.entrySet()) {
            System.out.println(o);
        }
    }

}
/** 结果
 String引用ref1,ref2,ref3,ref4 消失
 WeakHashMap GC之前
 obejct2=chaheObject2
 obejct1=chaheObject1
 WeakHashMap GC之后
 HashMap GC之前
 obejct4=chaheObject4
 obejct3=chaheObject3
 Disconnected from the target VM, address: '127.0.0.1:51628', transport: 'socket'
 HashMap GC之后
 obejct4=chaheObject4
 obejct3=chaheObject3
 **/

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200904184044946.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2traW5nX2VkYw==,size_16,color_FFFFFF,t_70#pic_center)

代码和图示主演演示WeakHashMap如何自动释放缓存对象,当init函数执行完成后,局部变量字符串引用weakd1,weakd2,d1,d2都会消失,此时只有静态map中保存中对字符串对象的引用,可以看到,调用gc之后,hashmap的没有被回收,而WeakHashmap里面的缓存被回收了。
8、监听器和回调如果客户端再你i实现的APi种注册回调,却没有显示的取消,那么就会积聚,需要确保回调立即被当作垃圾回收的最佳方法是之保存他的弱引用,例如将他们保存为WeakHashMap种的键

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值