今日看了网易公开课关于java内存的课程,觉得受益匪浅,虽然算是比较浅显的理论知识,但对于写程序来说还是帮助挺大的,毕竟只有掌握了内存的分配,才可以更好的写出性能高的java程序应用。所以还是决定写出来和各位分享这课程~~
长话短说,首先来看下整个内存总体模型(直接用笔画方便,不过字不好看= =):
简单说一下,最上方低地址的是一个特殊区域,一般存储静态变量、常量等。往下是堆(heap),主要存储在程序中动态创建和释放的实例,即对象。它是从低地址向高地址增长的。最下方高地址的是栈,存储像局部变量这样编译器自动分配的内存。有个问题,一个往上增长,一个往下,那它们会不会“相撞”呢?答案是“嗯,会的,一旦发生则会出现互相修改数据的情况= =”,但是这种事件发生的概率很小,暂时不做讨论。
那么java程序运行的时候内存的分配是什么样子的呢?咱举个例子看一看,以小见大,一目了然~~
上代码~~首先是Point类:
public class Point { private int x; private int y; public Point(int x, int y) { super(); this.x = x; this.y = y; } public void move(int dx,int dy){ x+=dx; y+=dy; }很简单,一个点,横纵坐标两个属性。move方法是堆点位置的移动。现在执行下面的方法:
public void run(){ Point p1 = new Point(2,3); Point p2 = new Point(4,5); p1.move(1, 2); }接下来就详细给大家说说内存是如何分配运作的~~
首先执行前两行语句,Point p1 = new Point(2,3);、Point p2 = new Point(4,5);,创建一个Point对象,此时内存是这样子滴:
左边堆存储对象,每个对象首先是一份额外的内存,然后是各个成员变量的内存空间,右边的栈是局部变量即对象引用p1、p2的内存空间(p1、p2为方法run的局部变量),同样起始也有一段额外内存空间,p1、p2的值为对应的对象在堆上的地址值,即引用指向对应的对象。
然后看看p1.move(1, 2);的执行,执行方法,首先在栈上开辟参数dx、dy变量的内存,还有一个this的内存,它是一个引用,指向被调用方法的对象(不然不知道哪个对象被调用= =),同样起始位置有一段额外空间:
然后x+=dx;y+=dy;被执行,内存变成:
堆上x,y的值被修改。注意move方法执行完毕后,栈上的对应的局部变量将出栈,即被释放。
加入现在有个线的类Line,代码如下:
public class Line { private Point beg; private Point end; public Line(Point beg, Point end) { this.beg = beg; this.end = end; } }run方法最后加一句Line line = new Line(p1, p2);,如下:
public void run(){ Point p1 = new Point(2,3); Point p2 = new Point(4,5); p1.move(1, 2); Line line = new Line(p1, p2); }执行这一句的时候,首先在栈上开辟Line的引用line,因为构造方法是方法,拥有局部p1、p2,故先在栈上开辟这两个参数的内存,然后在堆上开辟Line对象内存,两个成员变量是两个Point的引用,然后将栈上局部变量p1、p2的值赋给beg、end。如下图:
这样beg、end指向p1、p2指向的两个对象,随着构造方法执行完毕,Line的引用line也会指向Line对象,而局部变量p1、p2也会出栈,如下图:
当整个run方法执行完毕后,栈上的局部变量将全部出栈,堆上的对象仍然存在,直到垃圾回收器将其自动回收。
以上就是一个简单java程序执行对应的内存分配过程,希望大家看了有所收获,能起到抛砖引玉的效果就心满意足了。欢迎大家提出疑问或者指出里面有误的地方~~