对象内存管理
编译好的Java程序需要运行在JVM中
程序,无论代码还是数据,都需要存储在内存中。JVM为Java程序提供并管理所需要的内存空间
JVM内存主要分为 堆、栈、方法区 三个区域,还有一些其他的内存空间,比如下面讲的垃圾回收机制就存在其他内存中
堆内存
堆内存空间用于存储使用new关键字所创建的对象
成员变量的生命周期
访问对象需要依靠引用变量
当一个对象没有任何引用时,被视为废弃的对象,属于被回收的范围。该对象中所有成员变量也随之被回收。
成员变量的生命周期为:从对象在堆中创建开始到对象从堆中被回收结束
Airplane a = new Airplane();
a = null;
//不再指向刚分配的对象空间,成员变量失效
栈内存
栈用于存放方法中的局部变量
JVM在其内存空间开辟一个栈的存储空间
这部分空间用于存储程序运行时在方法中声明的所有局部变量
例如
//main()方法中有如下代码:
Airplane a = new Airplane();
int num = 5;
其在内存中体现为
栈内存中存放 num -->5、a --> 引用 ,堆内存中存放 a的引用指向的对象
局部变量的生命周期
一个运行的Java程序从开始到结束会有很多次方法的调用。JVM会为每一个方法的调用在栈中分配一个对应的空间,这个空间称为该方法的栈帧。
一个栈帧对应一个正在调用中的方法,栈帧中存储了该方法的参数、局部变量等数据。当某一个方法调用完成后,其对应的栈帧将被清除,局部变量失效。
方法区
- 方法存在方法区,并且是一个方法区就存一个方法;运行时会在栈中开辟一个栈帧,每运行一次就会为这个方法开辟一个栈帧,运行完方法后栈帧回收。类的信息存在方法区中(因为类中以方法为主),实例化的对象存在堆内存中。
- 方法区还用于存放类的信息,Java程序运行时,首先会通过类装载器载入类文件的字节码信息,经过解析后将其装入方法区。类的各种信息(包括方法)都在方法区存储。
Airplane a = new Airplane();
//Airplane 类首先被装载到JVM的方法区,其中包括类的基本属性和方法定义
方法只有一份
当类的信息被加载到方法区时,除了类的类型信息以外,同时类内的方法定义也被加载到方法区;
类在实例化对象时,多个对象会拥有各自在堆中的空间,但所有实例对象是共用在方法区中的一份方法定义的。
Airplane a1 = new Airplane();
Airplane a2 = new Airplane();
a1.step();
a2.step();
//step()方法只有一份,分别针对a1指向的对象和a2指向的对象调用了两次
成员变量和局部变量
1.局部变量
是声明在 方法 中的变量
没有默认值,必须自行设定初始值
只能在声明它的方法中使用,方法被调用时,存在栈中,方法调用结束,从栈中清除
2.成员变量
是声明在 类 中方法外的变量(属性)
有默认初始值,可以不显式初始化
能在类的内部中任何位置使用
所在类被实例化后,存在堆中,对象被回收时,成员变量失效
3.就近原则
成员变量和局部变量重名时
局部变量具有更高的优先级
如果想输出成员变量的值,需要在变量前加一个 this. 借指实例化对象本身
4.注意:方法中传递引用对象参数时
方法存在方法区,在栈中运行
方法运行后,在栈中将被清除掉,
基本数据类型在方法中传递的形参,在方法运行的栈中创建了一个局部变量
运行结束后删除,若无返回值,将无法给主函数(主函数是另一个方法)造成影响
但是注意引用类型,引用类型的引用存的是内存地址
传递形参时传递的是内存地址,该地址指向的是堆内存中的对象
如果在方法中通过改变这个引用形参指向对象的值,会对主函数中的引用对象造成影响
(基本数据类型传递的是具体数值,引用类型传递的是所存的地址)
方法与方法之间不会影响,但是要注意,如果是方法对成员变量的修改,将会与效果
垃圾回收机制
1.什么是垃圾回收管理机制(GC)
1.1 什么是垃圾
程序中使用过的,并且之后的程序不会再使用的对象或资源
java中堆内存只有一个,栈是每个方法对应一个
堆中存对象,栈中存引用、基本数据类型、局部变量等,所以一个方法对另一个方法的变量改变若无返回值不会对其他方法的变量造成影响,但是方法对堆内存中存的变量改变会造成影响
1.2 什么是垃圾回收,为什么需要
垃圾如果放任不管,会逐渐占满内存,造成内存溢出(内存泄漏)问题
回收垃圾,就是将不使用的资源或对象释放掉的操作
ps. 内存泄漏:
内存泄漏是指,不再使用的内存没有被及时的回收。严重的内存泄漏会因过多的内存占用而导致程序的崩溃。
GC线程判断对象是否可以回收的依据是该对象是否有引用指向,因此,当确定该对象不再使用时,应该及时将其引用设置为null。
Dog d = new Dog();
d = null;//刚实例化的对象,引用改为空,变为垃圾
1.3 垃圾回收管理机制
为了解决内存泄漏问题,java后台有个低线程在不断的扫描堆,如果发现堆中的这块区域,没有栈中的指针指向它,就会被认为是垃圾,被回收掉
垃圾回收器(Garbage Collection ,GC )就是JVM自带的一个回收垃圾的线程(自动运行着的程序) ,用于回收没有任何引用指向的对象。Java程序员不用担心内存管理,因为垃圾收集器会自动进行回收管理。
2.其他语言是如何回收垃圾的
C++: 没有垃圾回收机制
程序员要编写代码来释放对象或资源占用的内存
C++ 通过析构函数(与构造函数相反)来释放内存
3.垃圾回收机制的优点和不足
优点:
java程序员不需要考虑垃圾回收的问题
不足:(面试易考的)
- 垃圾回收管理机制本身是占用内存的
- 垃圾回收的时机并不是很及时(不会立即回收,是一段时间回收一次,因为有线程与线程的抢占问题)
- 垃圾回收管理机制只能回收java程序中产生的垃圾,在其他媒介(数据库,Windows文件等)中产生的垃圾要用 close 手动回收
4.垃圾的判定和原理
4.1 判定一个对象或资源是垃圾的规则有很多
4.2 引用计数法(多种规则之一):
内存中保存每个对象的引用个数,如果这个个数为0,那么为垃圾
4.3 如何标记一个对象为垃圾
在一个方法中,如果代码很长,对于某个对象只使用了一次,后续操作无用,需要设置为垃圾时
我们可以将该对象的 引用 = null;
5. finalize方法(不是关键字)
5.1 finalize是Object类中编写的一个方法
5.2 这个方法会被垃圾回收管理机制,在回收这个对象前调用(可以观察对象是否被回收)
5.3 这个方法时在垃圾回收前对垃圾进行处理的方法
6. System.gc() 方法
这个方法能通知垃圾回收机制,让它尽快来回收程序中的垃圾
当然,这个尽快赶来也是有延时的,所以只是通知,仍然不能立即回收垃圾,不是随叫随到
-
GC的回收对程序员来说是透明的,并不一定一发现有无引用的对象,就立刻回收。
-
一般情况下,当我们需要GC线程即刻回收无用对象时,可以调用 System.gc() 方法。
-
System.gc() 用于建议虚拟机马上调度GC线程回收资源,具体的实现策略取决于不同的JVM系统。
package day0722;
public class Dog {
//重写Object类中的finalize方法
public void finalize(){
System.out.println("该对象是垃圾,被回收了");
}
}
public class DogDemo {
public static void main(String[] args) {
Dog d = new Dog();
d = null;//刚实例化的对象,引用改为空,变为垃圾
//通知垃圾回收机制来帮忙收拾一下垃圾
System.gc();
System.out.println("88");
//大几率在这里垃圾被回收
}
}