JAVA内存管理 [转]
首先我们要明白一点,我们所使用的变量就是一块一块的内存空间!!
一、内存管理原理: 在java中,有java程序、虚拟机、操作系统三个层次,其中java程序与虚拟机交互,而虚拟机与操作系统间交互!这就保证了java程序的平台无关性!下面我们从程序运行前,程序运行中、程序运行内存溢出三个阶段来说一下内存管理原理! 1、程序运行前:JVM向操作系统请求一定的内存空间,称为初始内存空间!程序执行过程中所需的内存都是由java虚拟机从这片内存空间中划分的。 2、程序运行中:java程序一直向java虚拟机申请内存,当程序所需要的内存空间超出初始内存空间时,java虚拟机会再次向操作系统申请更多的内存供程序使用! 3、内存溢出:程序接着运行,当java虚拟机已申请的内存达到了规定的最大内存空间,但程序还需要更多的内存,这时会出现内存溢出的错误!至此可以看出,Java 程序所使用的内存是由 Java 虚拟机进行管理、分配的。Java 虚拟机规定了 Java 程序的初始内存空间和最大内存空间,开发者只需要关心 Java 虚拟机是如何管理内存空间的,而不用关心某一种操作系统是如何管理内存的。 二、 RUNTIME 类的使用: Java 给我们提供了Runtime 类得到JVM 内存的信息
| 方法名称 | 参数 | 作用 | 返回值 |
| getRuntime | 无 | 获取Runtime 对象 | Runtime 对象 |
| totalMemory | 无 | 获取JVM 分配给程序的内存数量 | long:内存数量 |
| freeMemory | 无 | 获取当前可用的内存数量 | long:内存数量 |
| maxMemory | 无 | 获取JVM 可以申请到的最大内存数量 | long:内存数量 |
public class Student { String stuId; String stuName; int stuAge; } public class TestStudent { public static void main(String[] args) { Student zhouxingxing = new Student(); String name = new String("旺旺"); int a = 10; char b = 'm'; zhouxingxing.stuId = "9527"; zhouxingxing.stuName = "周星星"; zhouxingxing.stuAge = 25; } }(1)类当然是存放在方法区里面的。 (2)Student zhouxingxing = new Student(); 这行代码就创建了两块内存空间,第一个在栈中,名字叫zhouxingxing,它就相当于指针类型的变量,我们看到它并不存放学生的姓名、年龄等具体的数值,而是存放堆中第二块内存的地址,第二块才存放具体的数值,如学生的编号、姓名、年龄等信息。 (3)int a = 10; 这是 基本数据类型 变量,具体的值就存放在栈中,并没有只指针的概念! 下图就是本例的内存布置图: 此外我们还要知道Student zhouxingxing = new Student(); 包括了声明和创建,即:Student zhouxingxing;和zhouxingxing = new Student();其中声明只是在栈中声明一个空间,但还没有具体的值,声明后的情况如下图所示: 创建后的情况如下图所示: (4)引用类型中的数组也封装了指针,即便是基本数据类型的数组也封装了指针,数组也是引用类型。比如代码int[] arr = new int[]{23,2,4,3,1};如下图所示: 五、java值传参与引用参数 (1)参数根据调用后的效果不同,即是否改变参数的原始数值,又可以分为两种:按值传递的参数与按引用传递的参数。 按值传递的参数原始数值不改变,按引用传递的参数原始数值改变!这是为什么呢?其实相当简单: 我们知道基本数据类型的变量存放在栈里面,变量名处存放的就是变量的值,那么当基本数据类型的变量作为参数时,传递的就是这个值,只是把变量的值传递了过去,不管对这个值如何操作,都不会改变变量的原始值。而对引用数据类型的变量来说,变量名处存放的地址,所以引用数据类型的变量作为传参时,传递的实际上是地址,对地址处的内容进行操作,当然会改变变量的值了! (2)特例:string
public class TestString { public static void main(String[] args) { String name = "wangwang"; TestString testString = new TestString(); System.out.println("方法调用前:" + name); testString.change(name); System.out.println("方法调用后:" + name); } void change(String str) { str = "旺旺老师"; System.out.println("方法体内修改值后:" + str); } }结果: 方法调用前:wangwang 方法体内修改值后:旺旺老师 方法调用后:wangwang 分析: 上例中,虽然参数String 是引用数据类型,但其值没有发生改变,这是因为String 类 是final 的,它是定长,我们看初始情况,即String name = "wangwang";这行代码运行 完,如下图: 当调用方法时testString.change(name),内存变化为: 在方法体内,参数str赋予一个新值,str = "旺旺老师"。因为String是定长,系统就会在堆中分配一块新的内存空间37DF,这样str指向了新的内存空间37DF,而name还是指向36DF, 37DF的改变对它已没影响: 最后,方法调用结束,str与37DF的内存空间消亡。Name的值依然为wangwang,并没有改变。 所以String虽然是引用类型参数,但值依然不变: (3)无法交换的例子:
public class TestChange {
void change(Student stu1, Student stu2) {
stu1.stuAge ++;
stu2.stuAge ++;
Student stu = stu1;
stu1 = stu2;
stu2 = stu;
}
public static void main(String[] args) {
Student furong = new Student();
furong.stuName = "芙蓉姐姐";
furong.stuAge = 30;
Student fengjie = new Student();
fengjie.stuName = "凤姐";
fengjie.stuAge = 26;
TestChange testChange = new TestChange();
testChange.change(furong, fengjie);
System.out.println(furong.stuName);
System.out.println(furong.stuAge);
System.out.println(fengjie.stuName);
System.out.println(fengjie.stuAge);
}
}
运行结果:芙蓉姐姐 31 凤姐 27 分析: