java虚拟机运行时内存分配图
栈 堆 方法区简介
1.栈
1. 每个方法被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等)
2. JVM为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量等)
3. 栈属于线程私有,不能实现线程间的共享!
4. 栈的存储特性是“先进后出,后进先出”
5. 栈是由系统自动分配,速度快!栈是一个连续的内存空间!
2. 堆
1. 堆用于存储创建好的对象和数组(数组也是对象)
2. JVM只有一个堆,被所有线程共享
3. 堆是一个不连续的内存空间,分配灵活,速度慢!
3.方法区
1. JVM只有一个方法区,被所有线程共享!
3. 用来存放程序中永远是不变或唯一的内容。(类信息【Class对象】、静态变量、字符串常量等)
示例代码
class Point {
private double x;
private double y;
Point(double x1, double y1) {
x = x1;
y = y1;
}
public double getX() { return x; }
public double getY() { return y; }
public void setX(double i) { x = i; }
public void setY(double i) { y = i; }
}
class Circle {
private Point o;
private double radius;
Circle(Point p, double r) {
o = p;
radius = r;
}
public void setO(double x, double y) {
o.setX(x);
o.setY(y);
}
public Point getO() { return o; }
public double getRadius() { return radius; }
public void setRadius(double r) { radius = r; }
public double area() { return 3.14 * radius * radius; }
Circle increaseRadius() {
radius++;
return this;
}
}
public class TestCircle {
public static void main(String args[]) {
Circle c1 = new Circle(new Point(1.0, 2.0), 2.0);
System.out.println("c1:" + c1.getO().getX());
c1.setO(5, 6);
System.out.println(c1.increaseRadius().increaseRadius().area());
}
}
一.解析构造函数 Circle c1 = new Circle(new Point(1.0,2.0), 2.0);
1. Circle c1
首先在栈内创建一个c1引用,指向地址不详
2. new Point(1.0,2.0)
在堆内创建一个Point对象 ,开始时Point内的变量X Y皆为0,
在栈内创建x1,y1,然后将其赋值给Point的x y,构造完成时内存分配如图
构造函数运行完成后,栈内x1,y1空间立刻释放
3.在堆内创建Circle对象 new Circle(new Point(1.0,2.0), 2.0)
同样,Circle对象刚创建时引用类型值为null,基础变量值为0,然后进入构造函数,在栈内创建一个指向刚才创建的Point对象的P和值为2.0的r,
然后用这两个元素初始化Circle内元素,使o指向Point,radius值变为2.0
构造函数完成后,栈内的r和p立刻释放
最后将c1指向Circle对象 Circle c1 = new Circle(new Point(1.0,2.0), 2.0);
二. c1.getO().getX()
首先调用geto获得c1所指向的Point,在栈中创建一个引用指向O引用
然后调用getX(),返回其X值,在栈内创建一临时对象用于存储X值
打印完成后,临时对象会被清理
三 c1.setO(5,6);
经过前面的学习,相信这个就很简单了,
在内存中创建临时变量x=5.0,y=6.0,然后通过赋值,改变c1内Point对象的值,SetO()结束后临时变量被清理.
四 c1.increaseRadius().increaseRadius().area() 关于this
首先c1调用increaseRadius()方法,返回一个Circle类的临时引用存储在栈内,指向c1对象,使radius变量+1.然后在通过这个临时引用再次调用increaseRadius()方法,使radius+1,再返回一个临时引用压栈,最后通过这个临时变量调用area()方法获取c1的增大半径后的面积.语句完成后两个栈内临时变量立刻被清理
static变量的内存分配
代码
class Cat {
public static int num = 0;
String name;
Cat(String name) {
this.name = name;
++num;
}
}
public class Main {
public static void main(String[] args) {
Cat c1 = new Cat("mimi");
Cat c2 = new Cat("miao");
System.out.println(Cat.num);
}
}
所有的字符串和static类型的静态成员变量都会预先加载于方法区内
所以上述代码执行时,内存分布如图,可以认为堆内c1,c2各有一引用指向方法区内对应元素
注
需要注意的是,栈内元素在使用完成后会立刻被清理,但是堆内创建的对象并不会立刻消失,而是在垃圾回收器启动回收后才会消失,而为了性能考虑,在内存充沛时,垃圾回收器并不会经常运行.所以可能某个元素已经空置了很久但是仍未被回收.
class Point {
int x, y;
Point(int _x, int _y) {
x = _x;
y = _y;
} //_x,_y在构造函数运行完成后会立刻消失
}
public class Main {
public static void main(String[] args) {
Point p1 = new Point(1, 2);
Point p2 = new Point(2, 3);
p1 = p2; //p1之前所指向的Point对象已经成为空置垃圾,但是不会被立刻回收
}
}