JAVA对象内存管理(暴力测试)

news/2024/7/20 12:49:28 标签: java, 内存泄漏, 内存管理, 堆栈

JAVA面向对象

JAVA对象内存管理栈和方法区解析

下一章节 栈和方法区解析
请打开此链接https://blog.csdn.net/QQ1043051018/article/details/112384674

对象内存管理

首先请大家时刻记住一个概览:

两室一厅、两室一厅、两室一厅

最大最灵活的是堆,其次是栈,这些都是随程序运行而不断变化的,最后是方法区,方法区相对固定

堆内存

在这里插入图片描述

堆内容(最灵活、也是唯一让程序员自动控制的空间,也是java占用绝大内存的部分)

堆是专门储存使用new关键字创建的对象实例的空间。
例如:Emp eric = new Emp();

程序员创建的每一个对象,地址都是储存在堆中,每new一次就开辟一次空间,而且每一次开辟都有一个唯一的地址值。

地址值一般都是存在栈中
在这里插入图片描述

生活案例:

现实中,大家去饭馆点菜,new运算就好像你点了一盘糖醋排骨,人厨师就给你做了一盘糖醋排骨端上来,堆就好像,你面前的一张大桌子,菜摆哪儿无所谓,只要摆的下就行,而栈就好像饥饿难耐的你一样,眼睛直勾勾的盯着
堆中的一个对象,也就是桌子上的这盘菜。


对象的生命周期:

在这里插入图片描述

什么是生命周期?

其实就是一个对象何时创建出来,又何时销毁的,也就是一个对象在内存中,从产生到结束的过程。

生活案例:就好像饭店吃完了饭,服务员开始收拾残羹剩饭。

如果一个对象被回收了,那么这个对象就消失没有了

JAVA程序的内存泄漏问题

我们先来一个小案例:

在这里插入图片描述

java">package day05;

public class Memory {
    public static void main(String[] args) {
        /*对象生命周期的测试*/
        //我点了一份小笼包,一笼4个
        char[] my = new char[]{'包','包','包','包'};
        //女朋友点了一份烧麦,一份5个
        char[] gf = new char[]{'烧','烧','烧','烧','烧'};
        //我吃完了所有的小笼包
        for (int i = 0; i<my.length; i++) {
            my[i]=0;
        }
        //女朋友只吃了2个烧麦
        gf[0]=0;
        gf[1]=0;
        System.out.println(gf);
        //我的包子吃完了,我去吃女朋友的烧麦
        System.out.println("我去吃女朋友的烧麦");
        my=gf; //把gf 堆中的地址值传给了 my
        //我吃了第三个烧麦
        my[2]=0;
        //女朋友吃了第四个烧麦
        gf[3]=0;
        System.out.println(gf);
    }
}

在练习案例中。我们会发现装小笼包的盘子已经空了,在生活中,我们一般会喊服务员收走,在java中,JVM自带一个垃圾回收机制,会帮我们把这个废弃的对象收走或者说删除掉

但是可能会发生延迟的情况,不能保证在第一时间被收走。
生活案例:就好比饭馆用餐高峰期,服务员忙不过来,不能第一时间把垃圾收走,垃圾没被收走,桌子也就无法使用,也就无法在接待新的客人。

这种情况就是一种资源浪费。

对于这种资源的浪费,在程序中,我们统称为“内存泄漏”。

这种内存泄漏不可避免,如果严重就麻烦了,会导致放弃对象堆积越来越多,内存空间越来越少,最终导致程序崩溃!

那么我们应该如何从自身小事做起,减少这种内存泄漏呢?

1.内存泄漏是指,废弃的对象没有被及时的回收。
2.严重的内存泄漏会导致内存中的废弃对象越来越多,直到内存占满程序崩溃。
3.垃圾回收器判断对象何时回收的依据是 “该对象是否还有变量正在引用”。
4.建议:确定一个引用变量指向的对象不再使用时,应该及时将引用类型变量设置为null。

注意:所以今后只要大家确定引用类型的变量 所指向的变量不在使用,就要立刻把这个对象赋值为null,也就是告诉JVM这个对象我不要了


我们还是以上面的例子 举例:
比如说:我女朋友吃完最后一个烧麦,准备离开了,那么内存中会怎么样呢?

java">package day05;

public class Memory {
    public static void main(String[] args) {
        /*对象生命周期的测试*/
        //我点了一份小笼包,一笼4个
        char[] my = new char[]{'包','包','包','包'};
        //女朋友点了一份烧麦,一份5个
        char[] gf = new char[]{'烧','烧','烧','烧','烧'};
        //我吃完了所有的小笼包
        for (int i = 0; i<my.length; i++) {
            my[i]=0;
        }
        //女朋友只吃了2个烧麦
        gf[0]=0;
        gf[1]=0;
        System.out.println(gf);
        //我的包子吃完了,我去吃女朋友的烧麦
        System.out.println("我去吃女朋友的烧麦");
        my=gf; //把gf 堆中的地址值传给了 my
        //我吃了第三个烧麦
        my[2]=0;
        //女朋友吃了第四个烧麦
        gf[3]=0;
        System.out.println(gf);
        //女朋友吃了最后一个烧麦,准备离开
        gf[4]=0;
        gf=null; //告诉JVM这个变量我不要了
        //仅将一个gf 变量设置为null是不够的,
        //因为my 也在调用这个变量,所有2个都要设置为null
        my=null;
    }
}

需要把my 和 gf 都设置为空,JVM才会认为此对象已经废弃,无对象调用,然后回收。


System.gc()方法

生活案例:
说道这里可能会有人问了,在饭馆里吃饭,我吃的多,菜摆不下了,可以自动喊服务员帮我收拾下桌子清理掉空盘子,那么在程序中应该怎么让JVM提前帮我收拾呢?

System.gc()方法: 建议JVM马.上调度GC尽快回收废弃对象。
注意了是建议!
gc回收是有延迟的,不能保证一发现,就立刻回收!

GC何时回收废弃对象,程序员是不必关心的。
GC并不是一发现废弃对象 ,就立刻回收,都会有延迟。
当废弃对象占用内存可能比较大时,就需要调用System.gc()方法。主动请GC立刻回收。
System.gc()方法: 建议JVM马.上调度GC尽快回收废弃对象。

例如:先获得当前JVM最大内存,初始内存,空闲内存

  • Runtime rt=Runtime.getRuntime();
    -最大内存( M ): rt.MaxMemory()/1024/1024;
    -初始内存( M ) : rt.TotalMemory()/1024/1024;
    -空闲内存( M ): rt.FreeMemory()/1024/1024;
    然后,循环创建200个10M大小的字符数组,观察创建前后的内存变化

java">package day05;

public class showMen {
    public static void main(String[] args) {
        //堆测试
        showMen();
    }

        /**
         * 显示JVM三大内存指标
         */
        public static void showMen() {
            //要想获得JVM三大指标,必须先获得一个对象叫做运行时对象
            Runtime rt = Runtime.getRuntime();
            //maxMemory得到的是一个字节单位,我们需要换算成兆M。
            //前面的"MAX"是理论值,加如说java内存不够了,我可能会系统要,最大要到多少。
            //而"Total"是,java虚拟机一启用 占多少内存。
            //"Free":空闲的内存有多大
            System.out.println("Max:" + (rt.maxMemory() / 1024 / 1024) + "M; "
                    + "Total" + (rt.totalMemory() / 1024 / 1024) + "M; "
                    + "Free" + (rt.freeMemory() / 1024 / 1024) + "M; ");
         }
    }

运行结果:
在这里插入图片描述
解析:

Max:1813M; Total123M; Free119M;
Max:1813M:其实是个理论值,都没有实际都分配,最大就只能1813M,在大程序就崩溃了(日常生活中经常看见 某某程序无响应,就是程序崩溃了)
Total123M:这是说现在JVM分配的内存为123M,当前123M可用
Free119M:这里指是在JVM分配的123M中,还可用的内存 也可以说还剩下119可用


练习:

创建一个10M的字符数组对象,然后观察 自动回收和不主动回收的差别。

java">package day05;

public class showMen {
    public static void main(String[] args) {
        //堆测试
        showMen();
        //那么这个10M的字符数组怎么定义呢?
        //数组的长度和大小 由元素的个数决定的。
        char[] chs=new char[10*1024*1024/2];//创建一个10M的char类型
        showMen();
    }

        /**
         * 显示JVM三大内存指标
         */
        public static void showMen() {
            //要想获得JVM三大指标,必须先获得一个对象叫做运行时对象
            Runtime rt = Runtime.getRuntime();
            //maxMemory得到的是一个字节单位,我们需要换算成兆M。
            //前面的"MAX"是理论值,加如说java内存不够了,我可能会系统要,最大要到多少。
            //而"Total"是,java虚拟机一启用 占多少内存。
            //"Free":空闲的内存有多大
           System.out.println("Max:" + (rt.maxMemory() / 1024 / 1024) + "M; "
                    + "Total" + (rt.totalMemory() / 1024 / 1024) + "M; "
                    + "Free" + (rt.freeMemory() / 1024 / 1024) + "M; ");
         }
    }

输出结果:
在这里插入图片描述

解析:

我们可以发现 Total还是123M,因为我们就调用了10M对他根本没影响,所以不改变。
我们在看发现 Free少了10M,因为我们现在程序有了10M的程序被占用。


让我们加上垃圾回收来看看:

java">public class showMen {
    public static void main(String[] args) {
        //堆测试
        showMen();
        //那么这个10M的字符数组怎么定义呢?
        //数组的长度和大小 由元素的个数决定的。
        char[] chs=new char[10*1024*1024/2];//创建一个10M的char类型

        //让我们清空内存调用,执行垃圾回收
        chs = null;
        System.gc();
        showMen();
    }

   //后面部分省略 和上面一样~

运行结果:
在这里插入图片描述
我们可以发现,内存已经回收成功,注意因为我们内存占用少,gc才能快速回收,如果内存占用过大,gc可能会发生内存泄漏

接下来暴力测试:

java">//堆测试
        showMen();
        //用循环创建测试 占用2000M,程序是否会崩溃
        for (int i = 0; i < 200; i++) {
            char[] chs=new char[10*1024*1024/2];
            //chs=null;
            //System.gc();
        }
        showMen();
    }
       //后面部分省略 和上面一样~

运行结果:
在这里插入图片描述
大家是不是很好奇,为什么没有崩溃?
在程序运行中,gc已经察觉到了内存可能紧张,于是自己主动启动垃圾回收,
另外我们可以发现Total和Free相差74M,是为什么呢? 是因为刚才在清理的过程中,要到了显示的时候,还剩下74M还没来得及回收,使用就占用了!

我们可以得出结论,如果大内存占用,仅靠JVM回收,很容易内存泄漏,程序崩溃,

继续测试

我们把char内存的大小增长到10000M呢?
自动回收还回收的过来吗?

java">public class showMen {
    public static void main(String[] args) {
        //堆测试
        showMen();
        //循环创建占用2000M,程序是否会崩溃
        for (int i = 0; i < 200; i++) {
            char[] chs=new char[10000*1024*1024/2];
            //chs=null;
            //System.gc();
        }
        showMen();
    }
    //后面部分省略 和上面一样~

运行结果:
在这里插入图片描述我们可以发现程序报错了!这是非常著名的错误!
表示堆里的内存溢出了!

注意:频繁的调用gc,会影响程序运行的效率。
就好像生活中,你正开心的吃着饭,服务员每隔5分钟来问你,“你吃完了吗?” ,是不是也会影响你吃饭的心情和效率呢?


下一章节 栈和方法区解析

请打开此链接https://blog.csdn.net/QQ1043051018/article/details/112384674


http://www.niftyadmin.cn/n/1756577.html

相关文章

网页三剑客,html/css/javascript

1、网页三剑客&#xff0c;html/css/javascript 引入到更高主流技术&#xff0c;进展比较快&#xff1b; 不是为学习这个知识&#xff0c;小于10%&#xff0c;高薪进入企业&#xff0c;新技术我们的目标是让大家做项目时&#xff0c;出现这些html标签&#xff0c;css样式&…

前端技术jQuery+Json+Ajax+NodeJS

jQuery 1.jQuery一统江湖10年&#xff0c;只是js封装&#xff0c;现今已经开始衰落&#xff0c;已经不作为重点&#xff0c;Vue替代(封装) Vue底层会使用一些jQuery语法。 在js上做了很多工作&#xff0c;代码更加简洁&#xff0c;新的特性 ajax封装 ajax它可以访问后台(jav…

前端技术Json+Ajax+NodeJS

一、jQuery json 1.1 概念 当今做软件已经不局限在一台机器上&#xff0c;很多台&#xff0c;如&#xff1a;联网游戏、电商网站背后都是服务器的集群&#xff0c;数十台到数万台的规模。那这些计算机要连接起来干大事&#xff0c;就必须做一件事。什么事呢&#xff1f;互相之…

JAVA对象内存管理栈和方法区解析

非堆一栈 JAVA对象内存管理 堆的解析 请打开此链接跳转到上一章: https://blog.csdn.net/QQ1043051018/article/details/112341273 栈专门用于存放方法中的局部变量 栈空间用于存储程&#xff0c;序运行时在方法中声明的所有局部变量。 局部变量:就是方法中声明的变量 包括…

JAVA入门之面向对象 继承和重写

继承 首先什么是继承呢? 继承就是使用一个类的定义&#xff0c;复制并扩展出一个新的类型&#xff0c;那么新的类型可以使用原来类型的属性和功能&#xff0c;也就是不劳而获。 当然新类型也可以扩展出&#xff0c;自己个性化的属性和功能&#xff0c;这就叫长江后浪推前浪&…

前端技术:Vue+MVVM框架+

早期开发&#xff0c;VB (Cliebt/Server,word)微软&#xff0c; 数据驱动Vue项目组件化 Vue概念 早期开发&#xff0c;VB (Client/Server/word) 微软&#xff0c; B/S (Broswer/Server) ASP,ASPCOM2000年&#xff0c;IIS(web中间件) .net C#抄袭JAVA java 2004&#xff0c;…

JAVA基础 访问控制

今天我们来聊一聊访问控制 什么是访问控制呢&#xff1f; 访问控制就是JAVA中控制类外的程序&#xff0c;能够访问类中的那些的成员。 有些人可能会问了&#xff0c;类的成员变量不是都能在外部访问吗&#xff1f; 其实不是的。 这和现实中一样。我们有很多属性也是不能对外公…

JAVA基础入门面向对象 抽象类

抽象类 什么是抽象类&#xff1f; 抽象类其实就是父类&#xff0c;抽象类中定义了所有子类应该共用的形式&#xff0c;但是并没有实现&#xff0c;而是靠各个子类去实现自己个性化的功能&#xff0c;也就是个性化定制。 那么什么是接口呢&#xff1f; 如果说这个抽象类。它还有…