千家信息网

java中内存管理关系及内存泄露原理的示例分析

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,这篇文章主要介绍java中内存管理关系及内存泄露原理的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!java内存管理关系及内存泄露原理这可能是最近写的博客中最接近底层的
千家信息网最后更新 2026年11月02日java中内存管理关系及内存泄露原理的示例分析

这篇文章主要介绍java中内存管理关系及内存泄露原理的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

    java内存管理关系及内存泄露原理

    这可能是最近写的博客中最接近底层的了。闲言少叙,进入正题。

    java对象和内存的关系

    首先,我们要知道下面几条真理(自己总结的)

    • 一个完整的建立对象流程是 1声明对象,2开辟内存空间,3将对象和内存空间建立联系。

    • 一个对象只能对应一个内存空间,一个内存空间可以对应很多对象

    • 回收一个内存空间 。如果,这个内存空间没有任何一个对象和他有联系。就可以被回收,或者好几个对象环形引用,也会被回收

    • 对一个对象进行操作的时候,是先通过 对象 找到 内存空间,然后 对内存空间进行操作(读,写 改 删)

    这是本人总结出来的四条经验。

    特别重要的是,一定要有这种认知。不管任何语言,最终都是要物理内存上面反映的,对象 和 内存空间 是两个不同的个体。 如果 没有的话,那么你会发现 下面将的都是什么啊!

    创建对象

       Stu one; //只声明 one对象 但是没有分配内存空间        //用new 开辟新的内存空间 oneMemory ,调用构造函数赋值,并将内存空间 oneMemory 与 one对象建立联系。        one = new Stu("one");                //声明 two对象 并开辟内存 twoMemory 调用构造函数赋值,并将内存空间 twoMemory与 two对象建立联系        Stu two = new Stu("two");                //声明 three对象, 并找到one 对象联系的内存空间 oneMemory。并将 oneMemory与 three 对象建立联系        Stu three = one;                //此时 内存空间 oneMemory 与两个对象有联系。一个是 one对象,一个是three对象        System.out.println("three 和 one 是否相等" + (three == one) + " one的哈希值" + one.hashCode() + " three的哈希值" + three.hashCode());

    运行结果

    我们可以发现,three对象 和one对象 指向的是同一个内存空间oneMemory。这个不就是符合上面所说的第二个真理 如果,我们对one对象进行操作,那么产生的影响,也会反映到three对象上。

    因为,**他们指向的是同一个内存空间,对one对象操作,就是做内存空间oneMemory进行操作。而three对象指向的也是oneMemory。这个符合上面第四条真理。**例子如下

    System.out.println("three 对象的值" + three.getName() + three.hashCode());        //修改one的值,第一步 找到one对象联系的内存空间 oneMemory , 将内存空间oneMemory 中的name值改变        one.setName("change");        //读取three对象值时候,先找到three对象联系的内存空间oneMemory,读取其中的name值        System.out.println("three 对象的值" + three.getName() + three.hashCode());

    null的作用

    长久以来,我只知道,将一个值复制成null,那么他就是空的了。但是 完全不知道,为啥。

    还是接着上面的例子,看一段代码;

     //读取three对象值时候,先找到three对象联系的内存空间oneMemory,读取其中的name值        System.out.println("three 对象的值 before" + three.getName() + three.hashCode());        /*         此时 如果 我们把one 对象 设置为null的。 对内存空间 oneMemory 是没有影响的         =null的作用是 将one对象自己本身 对内存空间的联系去除,并不会影响到内存空间和其他对象的联系         */        one = null;        System.out.println("three 对象的值  after" + three.getName() + three.hashCode());

    运行结果

    我们会发现 将one对象赋值为空后,three对象还是和先前一样。以前一直认为null,是将对象和他的内存空间清楚。但现在不是!。代码注释里面写的很清楚了。null 并不是清除内存空间,他只是把对象自己本身和内存空间的联系切断了

    内存泄露

    如果,你明白了上面。那么内存泄露对你来说也很简单了。

    再来学习一个新概念

    上面这么多呢。在本篇文章里面,你只需要记住 被static关键词修饰的变量,类,方法的生命周期是伴随整个程序的。就是程序活多久,他们就活多久

    接下来看代码

    首先,我们定义一个静态集合 staticList ,他是程序一运行,就会被创建好的。程序结束运行了,它才会被回收。

    static List staticList = new ArrayList<>();//开辟内存空间 listMemory
       System.out.println("three 对象的值  after" + three.getName() + three.hashCode());        /*         内存泄露 是长生命周期的对象 对一个内存空间有联系,造成内存空间没有办法被回收         */        /*        将three对象添加到静态集合里面,步骤是这样的,        第一步 找到three对象联系的内存空间 oneMemory        第二步 找到 staticList集合对象联系的内存空间 listMemory        第三步 告诉系统 staticList集合对象的部分成员 和内存空间 oneMemory 建立联系。         */        staticList.add(three);                /*        在这里 即使three对象已经和内存空间 oneMemory 没有联系了。        oneMemory 也不会被回收,因为上面说了内存空间和对象的关系是1对多。         而回收的条件是 一个内存空间没有一条和对象的联系才可以回收。         此时 内存空间 和staticList集合对象的部分成员 有联系,所以 内存空间不会被回收。         又由于staticList 集合对象联系的内存空间在 静态存储区,是伴随整个程序的。所以 在整个程序生命里面,         内存空间 oneMemory  就得不到 回收。  就是内存泄露了。         */        three = null;        System.out.println(staticList.get(0).hashCode());

    运行结果

    可以看见。在我们将three对象赋值null切断和内存空间 oneMemory的联系后。静态集合staticList对象的部分成员依然和内存空间 oneMemory有联系。根据上面第三条所说,因为内存空间 oneMemory 还是和对象有联系的(staticList)。所以不会回收oneMemory内存空间。又由于staticList是静态的,生命和程序一样长。 那么在整个程序周期里面,oneMemory内存空间 都不会被回收。就造成了内存泄露。

    附上完整的代码

    package com.zfh.test;import java.util.ArrayList;import java.util.List;public class JavaMain {    static List staticList = new ArrayList<>();//开辟内存空间 listMemory    public static void main(String[] args) {        Stu one; //只声明 one对象 但是没有分配内存空间        //用new 开辟新的内存空间 oneMemory ,调用构造函数赋值,并将内存空间 oneMemory 与 one对象建立联系。        one = new Stu("one");        //声明 two对象 并开辟内存 twoMemory 调用构造函数赋值,并将内存空间 twoMemory与 two对象建立联系        Stu two = new Stu("two");        //声明 three对象, 并找到one 对象联系的内存空间 oneMemory。并将 oneMemory与 three 对象建立联系        Stu three = one;        //此时 内存空间 oneMemory 与两个对象有联系。一个是 one对象,一个是three对象        System.out.println("three 和 one 是否相等" + (three == one) + " one的哈希值" + one.hashCode() + " three的哈希值" + three.hashCode());        System.out.println("three 对象的值" + three.getName() + three.hashCode());        //修改one的值,第一步 找到one对象联系的内存空间 oneMemory , 将内存空间oneMemory 中的name值改变        one.setName("change");        //读取three对象值时候,先找到three对象联系的内存空间oneMemory,读取其中的name值        System.out.println("three 对象的值 before" + three.getName() + three.hashCode());        /*         此时 如果 我们把one 对象 设置为null的。 对内存空间 oneMemory 是没有影响的         =null的作用是 将one对象自己本身 对内存空间的联系去除,并不会影响到内存空间和其他对象的联系         */        one = null;        System.out.println("three 对象的值  after" + three.getName() + three.hashCode());        /*         内存泄露 是长生命周期的对象 对一个内存空间有联系,造成内存空间没有办法被回收         */        /*        将three对象添加到静态集合里面,步骤是这样的,        第一步 找到three对象联系的内存空间 oneMemory        第二步 找到 staticList集合对象联系的内存空间 listMemory        第三步 告诉系统 staticList集合对象的部分成员 和内存空间 oneMemory 建立联系。         */        staticList.add(three);        /*        在这里 即使three对象已经和内存空间 oneMemory 没有联系了。        oneMemory 也不会被回收,因为上面说了内存空间和对象的关系是1对多。         而回收的条件是 一个内存空间没有一条和对象的联系才可以回收。         此时 内存空间 和staticList集合对象的部分成员 有联系,所以 内存空间不会被回收。         又由于staticList 集合对象联系的内存空间在 静态存储区,是伴随整个程序的。所以 在整个程序生命里面,         内存空间 oneMemory  就得不到 回收。  就是内存泄露了。         */        three = null;        System.out.println(staticList.get(0).hashCode());    }}

    bean对象 即Stu

    package com.zfh.test;public class Stu {  /*  static {        System.out.println("静态代码块 我只调用一次");    }*/      private String name;      /*  {        System.out.println("构造代码块");    }*/      public Stu(String name) {        this.name = name;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public void sout(){        System.out.println(this.name+this.hashCode());    }    public void printer() {        System.out.println(Stu.class.hashCode());    }    @Override    protected void finalize() throws Throwable {        super.finalize();        System.out.println("终结了");    }}

    检测内存泄露的原理

    检测内存泄漏的关键是要能截获住对分配内存和释放内存的函数的调用。截获住这两个函数,我们就能跟踪每一块内存的生命周期,比如,每当成功的分配一块内存后,就把它的指针加入一个全局的list中;每当释放一块内存,再把它的指针从list中删除。这样,当程序结束的时候,list中剩余的指针就是指向那些没有被释放的内存。这里只是简单的描述了检测内存泄漏的基本原理,详细的算法可以参见Steve Maguire的<>。  

    如果要检测堆内存的泄漏,那么需要截获住malloc/realloc/free和new/delete就可以了(其实new/delete最终也是用malloc/free的,所以只要截获前面一组即可)。对于其他的泄漏,可以采用类似的方法,截获住相应的分配和释放函数。比如,要检测BSTR的泄漏,就需要截获SysAllocString/SysFreeString;要检测HMENU的泄漏,就需要截获CreateMenu/ DestroyMenu。(有的资源的分配函数有多个,释放函数只有一个,比如,SysAllocStringLen也可以用来分配BSTR,这时就需要截获多个分配函数)。

    在Windows平台下,检测内存泄漏的工具常用的一般有三种,MS C-Runtime Library内建的检测功能;外挂式的检测工具,诸如,Purify,BoundsChecker等;利用Windows NT自带的Performance Monitor。这三种工具各有优缺点,MS C-Runtime Library虽然功能上较之外挂式的工具要弱,但是它是免费的;Performance Monitor虽然无法标示出发生问题的代码,但是它能检测出隐式的内存泄漏的存在,这是其他两类工具无能为力的地方。

    以上是"java中内存管理关系及内存泄露原理的示例分析"这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注行业资讯频道!

    内存 对象 空间 联系 程序 函数 检测 静态 分配 代码 就是 生命 并将 原理 周期 对内 工具 成员 时候 部分 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网 科技创业 互联网科技媒体 互联网与未来科技 长亮科技互联网金融 深圳卡尔迅互联网科技有限公司怎么样 银川新华互联网科技学校地址 互联网教育科技公司vipkid 晓花互联网科技有限公司网址 银川新华互联网科技学校联系电话 河南住房之家互联网科技 佛山市翊博互联网络科技有限公司 朱啸虎曾投资哪些互联网科技企业 明优互联网科技有限公司 中国互联网有什么科技 风投北京互联网科技 互联网科技包装股份有限公司上市 广西科技产业互联网是什么 互联网科技创业有什么好项目 六子台科技互联网展会 广州全民伙伴互联网科技有限公司 科技类互联网 天津摩尔互联网科技 镁科技互联网资讯平台 互联网科技有哪些公司 互联网科技研发水平 雨阳互联网卡科技有限公司 南京一网一红互联网科技有限公司 新氧科技和美国互联网有关系吗 科技互联网行业投资研究 亚光科技低轨卫星互联网
    0