内存管理图解
内存管理:
由JVM管理,JVM会把内存分成5部分:寄存器、外部方法区、方法区、堆、栈,这里主要看后三种
堆:
- 堆中存储的是new出来的对象(包括实例变量)
- 垃圾:垃圾就是指没有任何引用所指向的对象
垃圾回收器(GC) 会不定时到内存中清扫垃圾,
回收过程是透明的(看不到的),
不一定一发现垃圾就立刻回收,
调用System.gc()可以建议虚拟机尽快调度GC来回收 -
内存泄漏:不再使用的内存没有被及时的回收,就会一直占用内存。
建议:当对象不再使用时及时将引用设置为null,否则就会出现内存泄漏的现象,导致越来越卡 - 实例变量的生命周期:
创建对象时存储在堆中,对象被回收时一并被回收
栈:
- 栈中存储的是正在调用的方法中的所有局部变量(包括方法参数)
- 调用方法时会在栈中为该方法分配对应的栈帧,
栈帧中存储方法中的局部变量(包括方法的参数),
方法调用结束时,栈帧被清除,局部变量一并失效,清除时遵循先进后出的顺序 - 局部变量的生命周期:
方法被调用时存储在栈中,方法结束时与栈帧一并被清除
方法区:
- 方法区中存储.class字节码文件(包括静态变量、方法)
- 方法只有一份(存在方法区中),不同对象的调用通过this来区分具体的调用对象
class Aoo{
int a;
void showA(){
System.out.println(a);
}
}
class Boo extends Aoo{
void showB(){}
}
public void main(String[] args){
Boo bo = new Boo();
bo.showA();
bo.showB();
Aoo ao = (Aoo)bo;
ao.showA();
ao.showB();//编译错误,ao是Aoo类型,只能调用showA
}
以这段代码为例展示一下内存分配情况:
- 首先Aoo和Boo两个方法编译后生成.class文件都存在方法区中,存有类型信息、方法的定义等,实例化的对象使用的方法都是这里的方法,仅此一份。
- 然后到main方法,栈内存会为main开辟一段内存称作栈帧,新建了一个boo类型的变量bo,这个是局部变量,存在栈中main栈帧中,new了一个Boo对象存在堆内存中,对象实例化时会调用构造函数,此时调用Boo的构造函数,读取构造函数又会在栈内存中为Boo()开辟一块栈帧,默认构造函数里会给a赋默认值0,完整写法为:
this.a = 0;
。this其实就是一个地址,指向了堆中调用当前Boo函数的对象,于是就给这个对象的a变量赋值为0。此时Boo()结束,出栈。3. bo是Boo类型的变量,showA和showB都能调用
调用时showA先进栈,showA里有个println函数,会把this.a当做参数赋值给println的变量,然后执行、出栈,然后showB进栈,showB出栈
4.解释一下为什么bo和ao能调用的方法不一样:
程序在编译器会检查语法错误,编译期.class已经进入了方法区,但是栈和堆中没有东西,所以要判断变量能调用什么方法只能去它对应的数据类型中找,bo是Boo类型,所以能调用Boo里的所有东西,而Boo继承了Aoo,自然Boo里也会有showA方法,这是bo.showA()可行的原因。但是Aoo中却没有showB(),所以Aoo类型的ao只能调用Aoo里的方法,所以ao.showB会报错。