程序与内存
程序要想运行,一般需要以下三个步骤,如图1-1所示:
1、将程序加载到内存区。
2、操作系统代码找到程序的main方法开始执行。
3、执行过程中进行内存管理。
图1-1 程序执行过程
一个程序的内存分析
比如我们写了一个求空间中两点之间距离的程序。很简单,只要一个点对象就可以完成了。代码如下:
class Point {
double x, y, z;
Point(double _x, double _y, double _z) {
x = _x;
y = _y;
z = _z;
}
void setX(double _x) {
x = _x;
}
double getDistance(Point p) {
return (x - p.x)*(x - p.x) + (y - p.y)*(y - p.y) + (z - p.z)*(z - p.z);
}
}
同样,也需要一个类来使用Point类,我们将这个使用过程写在了main函数中,代码如下:
public class TestPoint {
public static void main(String[] args) {
Point p = new Point(1.0, 2.0, 3.0);
Point p1 = new Point(0.0, 0.0, 0.0);
//计算点p到原点的距离
System.out.println(p.getDistance(p1));
//设置点的x坐标为5
p.setX(5.0);
//计算点p到点(1.0,1.0,1.0)的距离
System.out.println(p.getDistance(new Point(1.0, 1.0, 1.0)));
}
}
我们开始看第一句代码:Point p = new Point(1.0, 2.0, 3.0);
,分析其执行过程和相应的内存变化。它首先可以分成两个部分:①调用构造方法生成Point对象;②声明Point对象并为其赋值。new Point(1.0, 2.0, 3.0)
的过程其实就是在堆内存中新建了一个Point对象,里面有三个成员变量(x = 1.0 ,y = 2.0 ,z = 3.0)。然后再执行声明和赋值,Point p
即Point对象的声明过程,在内存的栈空间中分配一块儿内存,通过名字p调用,为其赋值的过程即:将堆中new出来的对象的引用保存到这块栈空间中。内存状态如图2-1所示:
图2-1 第一句代码执行完成后内存图
第二句代码的分析过程同一类似,不再赘述,执行完成后内存空间状态如图2-2所示:
图2-2 第二句代码执行完成后内存图
第三句代码:
System.out.println(p.getDistance(p1))
,其中println是字符串的打印输出方法,而括号中是点对象方法的调用,它们的过程是一样的,那我们直接分析括号中的方法调用过程,即:p.getDistance(p1)。
我们可以看到
getDistance(Point p)
方法中有一个参数Point p,p也叫局部变量。当我们调用这个方法的时候,在栈内存中会分配一直临时变量p,指向p1的引用对象。计算完成后,会有一个返回值,内存中的栈空间会分配一块儿空间来存储这个返回值。这时方法调用完毕,为其局部变量分配的内存空间收回,即局部变量p消失。当System.out.println()执行完毕,将返回值打印在界面上,为返回值分配的内存消失。所以第三句代码执行完成之后,内存空间无变化。(它曾经来过,它现在没了)
第四句代码:
p.setX(5.0);
,调用setX()方法将p引用的对象的x值修改为5.0,此时内存空间状态如图2-3所示:
图2-3 第三句代码执行完成后内存图
最后一句代码:
System.out.println(p.getDistance(new Point(1.0, 1.0, 1.0)));
。遇到稍复杂的表达式,我们应从里向外分析。
new Point(1.0, 1.0, 1.0)
也就是在堆中新建一个点对象,里面有三个成员变量(x=1.0 , y=1.0 ,z=1.0)。然后调用对象p的
getDistance()
方法,新建一个临时变量p(参数),指向刚new出来的这对象,计算结果,返回并打印计算结果,方法执行完毕,为局部变量p和返回值分配的内存空间消失。堆中的点对象(1.0,1.0,1.0)没有引用指向它,等待垃圾收集器回收。此时内存空间状态如图2-4所示:
图2-4 最后一句代码执行完成后内存图
main方法执行完成后,局部变量p和p1也就消失了,其引用的对象等待垃圾收集器回收。
总结
- 基本数据类型只在栈中分配一块空间。
- 引用类型在栈中和堆中各有一块内存空间。
- 方法执行完毕,为其分配的局部变量消失,栈中的返回值如果没有用到也会默默的消失。
- 垃圾收集器判断一个对象是否需要回收的标准是:有没有引用指向它。如果没有,则回收。
- 字符串常量在DataSegment和栈中分配内存空间,相同字符串常量的引用相同(java优化机制)。
- 字符串变量在堆栈中分配内存,相同字符串的引用不同,并重写了equals()方法,以方便比较字符串内容是否一致。