Java对象内存管理

1 对象内存管理

1.1 Java 内存管理规则

        在JAVA中,有java程序、虚拟机、操作系统三个层次,其中java程序与虚拟机交互,而虚拟机与操作系统交互。编译好的java字节码文件运行在JVM中。

        程序中无论代码还是数据,都需要存储在内存中,而java程序所需内存均由JVM进行管理分配,开发者只需关心JVM是如何管理内存的,而无需关注某种操作系统是如何管理内存的,这就保证了java程序的平台无关性。由于Java内存是自动管理的,一般情况下不用人工干预。只有了解内存管理规则才能写出高质量的代码。

        JVM会将申请的内存从逻辑上划分为三个区域:堆、栈、方法区。这三个区域分别用于存储不同的数据:

        1、方法区是一个静态区,Java的类(.class)以及类中方法都加载到这个区域,在使用类之前Java会将类自动加载到方法区,只加载一次。

        2、栈是Java局部变量的空间,局部变量是指在方法中声明的变量,包括方法参数和this都是局部变量。在方法运行期间所有局部变量都在栈中分配,当方法结束时候方法中分配的局部变量全部销毁。

        3、堆是Java对象空间,Java的全部对象都在堆中分配,按照对象的属性在堆内存中分配对象的存储空间。对象使用以后,当对象不再被引用时候,对象变成内存垃圾,Java垃圾回收器会自动回收内存垃圾。

1.2 创建对象的过程

        短短一行的创建对象的代码,实际上发生了很多的事情,涉及到堆、栈、方法区这三大内存空间。

        如下图所示:

        具体流程如下:

  • 把Memory.class文件和Person.class文件加载进内存的方法区
  • 在栈内存中,开辟空间,存放变量p
  • 在堆内存中,开辟空间,存放Person对象
  • 对成员变量进行默认的初始化
  • 对成员变量进行显式初始化
  • 执行构造方法
  • 堆内存完成
  • 把堆内存的地址值赋值给变量p ,p就是一个引用变量,引用了Person对象的地址值

2 堆内存

2.1 对象存储在堆中

        JVM在其内存空间开辟了一个称为“堆”的存储空间,这部分空间用于存储使用new关键字所创建的对象。请看如下代码:

Person   p  = new  Person ();

        其内存分布如下图所示:

        从上图中可以看到右侧的堆内存,new Person()所创建的对象在堆中分配,同时成员变量亦在此分配,并赋初始值为零。引用类型变量p在栈内存中分配,其中保存的数据,为对象在堆内存中的地址信息,假设对象在堆内存的地址为40DF,则p中保存的即是40DF。

2.2 成员变量的生命周期

        当声明好对象之后,对该对象(堆中的Person)的访问需要依靠引用变量(栈中的p),那么当一个对象没有任何引用时,该对象被视为废弃的对象,属于被回收的范围,同时该对象中的所有成员变量也随之被回收。

        可以这样认为,成员变量的生命周期为:从对象在堆中创建开始到对象从堆中被回收结束。

        请看如下的代码,演示了对象不再被引用:

Person   p  =  new  Person();
p = null ;  
//不再指向刚分配的对象空间,成员变量失效

2.3 垃圾回收机制

        垃圾回收器(Garbage Collection,GC)是JVM自带的一个线程(自动运行着的程序),用于回收没有任何引用所指向的对象。

        GC线程会从栈中的引用变量开始跟踪,从而判定哪些内存是正在使用的,若GC无法跟踪到某一块堆内存,那么GC就认为这块内存不再使用了,即为可回收的。但是,java程序员不用担心内存管理,因为垃圾收集器会自动进行管理。

2.4 Java程序的内存泄露问题

        内存泄露是指,不再被使用的内存没有被及时的回收,严重的内存泄露会因过多的内存占用而导致程序的崩溃。在程序中应该尽量避免不必要的内存浪费。

        GC线程判断对象是否可以被回收的依据是该对象是否有引用来指向,因此,当确定该对象不再使用时,应该及时的将其引用设置为null,这样,该对象即不再被引用,属于可回收的范围。

2.5 System.gc()方法

        GC的回收对程序员来说是透明的,并不一定一发现有无引用的对象就立即回收。一般情况下,当我们需要GC线程即刻回收无用对象时,可以调用System.gc()方法。此方法用于建议JVM马上调度GC线程回收资源,但具体的实现策略取决于不同的JVM系统。

3 栈内存

3.1 栈用于存放方法中的局部变量

        JVM在其内存空间开辟一个称为”栈”的存储空间,这部分空间用于存储程序运行时在方法中声明的所有的局部变量,例如,在main方法中有如下代码:

Person   p  = new  Person ();

int    num  =  5;

        其内存分配如下图 所示:

        说明:方法中的变量即为局部变量,是在栈内存中分配,若变量为值类型,则在栈中存储的就是该变量的值。若变量为引用类型,则在栈中存储的是堆中对象的地址。

3.2 局部变量的生命周期

        一个运行的Java程序从开始到结束会有多次方法的调用。JVM会为每一个方法的调用在栈中分配一个对应的空间,这个空间称为该方法的栈帧。一个栈帧对应一个正在调用中的方法,栈帧中存储了该方法的参数、局部变量等数据。当某一个方法调用完成后,其对应的栈帧将被清除,局部变量即失效。

3.3 成员变量和局部变量

        成员变量与局部变量的差别如下:

        局部变量:

  • 定义在方法中
  • 没有默认值,必须自行设定初始值
  • 方法被调用时,存在栈中,方法调用结束时局部变量从栈中清除

        成员变量:

  • 定义在类中,方法外
  • 由系统设定默认初始值,可以不显式初始化
  • 所在类被实例化后,存在堆中,对象被回收时,成员变量失效

4 方法区

4.1 方法区用于存放类的信息

        方法区用于存放类的信息,Java程序运行时,首先会通过类装载器载入类文件的字节码信息,经过解析后将其装入方法区。类的各种信息(包括方法)都在方法区存储,看如下代码:

Person   p  = new  Person ();

        程序在执行这句话时,类首先被装载到JVM的方法区,其中包括类的基本信息和方法定义等,如下图所示:

        通过图示可以看出,在方法区中,包含Person类的字节码文件,及类的基本信息及方法M1等。

4.2 方法只有一份

        当类的信息被加载到方法区时,除了类的类型信息以外,同时类内的方法定义也被加载到方法区。

        类在实例化对象时,多个对象会拥有各自在堆中的空间,但所有实例对象是共用在方法区中的一份方法定义的。意味着,方法只有一份。看如下代码:

Person p1 = new Person();

Person p2 = new Person();

p1.M1( );

p2.M1( );

        如上的代码中,对象有两个,但是M1方法只有一份,分别针对p1指向的对象和p2指向的对象调用了两次。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhangyan_1010

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值